MXVK Vulkan Framework 0.35.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
post.cpp
Go to the documentation of this file.
1#include "mxvk/argz.hpp"
2#include "mxvk/mxvk.hpp"
4
5#include <SDL3/SDL.h>
6
7#include <algorithm>
8#include <array>
9#include <cctype>
10#include <cstdint>
11#include <cstdlib>
12#include <filesystem>
13#include <format>
14#include <fstream>
15#include <iostream>
16#include <memory>
17#include <ranges>
18#include <sstream>
19#include <string>
20#include <vector>
21
22namespace example {
24 void operator()(SDL_Surface *surface) const {
25 if (surface != nullptr) {
26 SDL_DestroySurface(surface);
27 }
28 }
29 };
30
32 public:
33 PostprocessWindow(const std::string &path, int width, int height, bool fullscreen, bool enable_vsync) : mxvk::VK_Window("MXVK Postprocess Chain", width, height, fullscreen, MXVK_VALIDATION, enable_vsync), asset_root((path.empty() || path == ".") ? std::string(postprocess_ASSET_DIR) : path) {
34 resizeCanvas(width, height);
35 sprite = createSprite(canvas.get());
36 attachEffects(loadEffects(asset_root + "/data/shaders.txt"));
37 }
38
39 void event(SDL_Event &e) override {
40 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
41 exit();
42 }
43 if (e.type == SDL_EVENT_QUIT) {
44 exit();
45 }
46 }
47
48 void proc() override {
49 const VkExtent2D extent = getSwapchainExtent();
50 const int targetWidth = extent.width > 0U ? static_cast<int>(extent.width) : canvas_width;
51 const int targetHeight = extent.height > 0U ? static_cast<int>(extent.height) : canvas_height;
52 if (targetWidth != canvas_width || targetHeight != canvas_height) {
53 resizeCanvas(targetWidth, targetHeight);
54 }
55
56 drawCanvas();
57 if (sprite != nullptr) {
58 sprite->updateTexture(canvas.get());
59 sprite->drawSpriteRect(0, 0, canvas_width, canvas_height);
60 }
61 }
62
63 private:
64 static std::string trim(std::string value) {
65 const auto first = std::ranges::find_if(value, [](unsigned char ch) { return !std::isspace(ch); });
66 const auto last = std::ranges::find_if(value | std::views::reverse, [](unsigned char ch) { return !std::isspace(ch); }).base();
67 if (first >= last) {
68 return {};
69 }
70 return std::string(first, last);
71 }
72
73 std::vector<mxvk::VK_Window::PostProcessingEffect> loadEffects(const std::string &manifestPath) const {
74 std::ifstream file(manifestPath);
75 if (!file.is_open()) {
76 throw mxvk::Exception("postprocess: failed to open " + manifestPath);
77 }
78
79 std::vector<mxvk::VK_Window::PostProcessingEffect> effects;
80 std::string line;
81 while (std::getline(file, line)) {
82 const size_t comment = line.find('#');
83 if (comment != std::string::npos) {
84 line.resize(comment);
85 }
86 line = trim(line);
87 if (line.empty()) {
88 continue;
89 }
90
91 std::filesystem::path shader_path(line);
92 if (shader_path.is_relative()) {
93 shader_path = std::filesystem::path(asset_root) / "data" / shader_path;
94 }
95 if (!std::filesystem::exists(shader_path)) {
96 throw mxvk::Exception("postprocess: shader listed in shaders.txt was not found: " + shader_path.string());
97 }
98
99 mxvk::VK_Window::PostProcessingEffect effect{};
100 effect.fragmentShaderPath = shader_path.string();
101 effect.params = {0.0f, 1.0f, 1.0f, 0.0f};
102 effect.timeEnabled = true;
103 effects.push_back(effect);
104 }
105
106 return effects;
107 }
108
109 void attachEffects(const std::vector<mxvk::VK_Window::PostProcessingEffect> &effects) {
110 if (effects.empty()) {
112 return;
113 }
114
116 for (size_t i = 0; i < effects.size(); ++i) {
117 setPostProcessingShaderTimeEnabled(i, effects[i].timeEnabled);
118 }
120 std::cout << std::format("postprocess: attached {} post-processing effect(s)\n", effects.size());
121 }
122
123 void resizeCanvas(int width, int height) {
124 if (width <= 0 || height <= 0) {
125 return;
126 }
127 if (canvas != nullptr && canvas_width == width && canvas_height == height) {
128 return;
129 }
130
131 canvas.reset(SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32));
132 if (canvas == nullptr) {
133 throw mxvk::Exception("postprocess: failed to create SDL surface: " + std::string(SDL_GetError()));
134 }
135 canvas_width = width;
136 canvas_height = height;
137 }
138
139 void drawCanvas() {
140 if (canvas == nullptr || !SDL_LockSurface(canvas.get())) {
141 return;
142 }
143
144 ++frame;
145 auto *pixels = static_cast<std::uint8_t *>(canvas->pixels);
146 for (int y = 0; y < canvas_height; ++y) {
147 std::uint8_t *row = pixels + y * canvas->pitch;
148 for (int x = 0; x < canvas_width; ++x) {
149 const float nx = static_cast<float>(x) / static_cast<float>(std::max(canvas_width - 1, 1));
150 const float ny = static_cast<float>(y) / static_cast<float>(std::max(canvas_height - 1, 1));
151 const int checker = ((x / 48) + (y / 48)) & 1;
152 const std::uint8_t red = static_cast<std::uint8_t>(40.0f + nx * 180.0f);
153 const std::uint8_t green = static_cast<std::uint8_t>(50.0f + ny * 160.0f);
154 const std::uint8_t blue = static_cast<std::uint8_t>(checker != 0 ? 220 : 80);
155 std::uint8_t *pixel = row + x * 4;
156 pixel[0] = static_cast<std::uint8_t>((red + frame) & 0xFF);
157 pixel[1] = green;
158 pixel[2] = blue;
159 pixel[3] = 255;
160 }
161 }
162
163 SDL_UnlockSurface(canvas.get());
164 }
165
166 std::string asset_root;
167 mxvk::VK_Sprite *sprite = nullptr;
168 std::unique_ptr<SDL_Surface, SurfaceDeleter> canvas;
169 int canvas_width = 1280;
170 int canvas_height = 720;
171 std::uint8_t frame = 0;
172 };
173} // namespace example
174
175int main(int argc, char **argv) {
176 try {
177 Arguments args = proc_args(argc, argv);
178 example::PostprocessWindow window(args.path, args.width, args.height, args.fullscreen, args.enable_vsync);
179 window.loop();
180 } catch (const mxvk::Exception &e) {
181 std::cerr << std::format("mxvk: Exception: {}\n", e.text());
182 return EXIT_FAILURE;
183 } catch (const ArgException<std::string> &e) {
184 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
185 return EXIT_FAILURE;
186 }
187 return EXIT_SUCCESS;
188}
Lightweight, header-only, template command-line argument parser.
Arguments proc_args(int &argc, char **argv)
Parse standard libmx2 command-line options from main()'s argv.
Definition argz.hpp:854
Exception thrown by Argz::proc() on unrecognised or malformed options.
Definition argz.hpp:169
void event(SDL_Event &e) override
Handle one SDL event.
Definition post.cpp:39
void proc() override
Execute one processing/update step.
Definition post.cpp:48
PostprocessWindow(const std::string &path, int width, int height, bool fullscreen, bool enable_vsync)
Definition post.cpp:33
std::string text() const
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:39
VkExtent2D getSwapchainExtent() const noexcept
Get the current swapchain extent.
Definition mxvk.hpp:222
void loop()
Run the main event/render loop.
Definition mxvk.cpp:651
VK_Sprite * createSprite(const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a sprite from a PNG file and register it with this window.
Definition mxvk.cpp:4455
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
Definition mxvk.cpp:1474
void exit()
Request loop termination.
Definition mxvk.cpp:1378
VK_Window()=default
Construct an empty window object.
void setPostProcessingEnabled(bool enabled)
Definition mxvk.hpp:348
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
Definition mxvk.cpp:1612
#define MXVK_VALIDATION
Definition mxvk.hpp:28
int main()
Definition main.py:165
Utilities for loading and saving PNG images.
Definition mxvk.hpp:31
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:718
bool fullscreen
Whether fullscreen mode was requested.
Definition argz.hpp:724
bool enable_vsync
Enable FIFO present mode / v-sync (--enable-vsync).
Definition argz.hpp:738
int height
Viewport height in pixels (default: 720).
Definition argz.hpp:721
std::string path
Asset root; proc_args() defaults it to the executable directory.
Definition argz.hpp:723
int width
Viewport width in pixels (default: 1280).
Definition argz.hpp:720
void operator()(SDL_Surface *surface) const
Definition post.cpp:24
std::array< float, 4 > params
Definition mxvk.hpp:310