MXVK Vulkan Framework 0.24.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)
34 : mxvk::VK_Window("MXVK Postprocess Chain", width, height, fullscreen, MXVK_VALIDATION, enable_vsync),
35 asset_root((path.empty() || path == ".") ? std::string(postprocess_ASSET_DIR) : path) {
36 resizeCanvas(width, height);
37 sprite = createSprite(canvas.get());
38 attachEffects(loadEffects(asset_root + "/data/shaders.txt"));
39 }
40
41 void event(SDL_Event &e) override {
42 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
43 exit();
44 }
45 if (e.type == SDL_EVENT_QUIT) {
46 exit();
47 }
48 }
49
50 void proc() override {
51 const VkExtent2D extent = getSwapchainExtent();
52 const int targetWidth = extent.width > 0U ? static_cast<int>(extent.width) : canvas_width;
53 const int targetHeight = extent.height > 0U ? static_cast<int>(extent.height) : canvas_height;
54 if (targetWidth != canvas_width || targetHeight != canvas_height) {
55 resizeCanvas(targetWidth, targetHeight);
56 }
57
58 drawCanvas();
59 if (sprite != nullptr) {
60 sprite->updateTexture(canvas.get());
61 sprite->drawSpriteRect(0, 0, canvas_width, canvas_height);
62 }
63 }
64
65 private:
66 static std::string trim(std::string value) {
67 const auto first = std::ranges::find_if(value, [](unsigned char ch) {
68 return !std::isspace(ch);
69 });
70 const auto last = std::ranges::find_if(value | std::views::reverse, [](unsigned char ch) {
71 return !std::isspace(ch);
72 }).base();
73 if (first >= last) {
74 return {};
75 }
76 return std::string(first, last);
77 }
78
79 std::vector<mxvk::VK_Window::PostProcessingEffect> loadEffects(const std::string &manifestPath) const {
80 std::ifstream file(manifestPath);
81 if (!file.is_open()) {
82 throw mxvk::Exception("postprocess: failed to open " + manifestPath);
83 }
84
85 std::vector<mxvk::VK_Window::PostProcessingEffect> effects;
86 std::string line;
87 while (std::getline(file, line)) {
88 const size_t comment = line.find('#');
89 if (comment != std::string::npos) {
90 line.resize(comment);
91 }
92 line = trim(line);
93 if (line.empty()) {
94 continue;
95 }
96
97 std::filesystem::path shader_path(line);
98 if (shader_path.is_relative()) {
99 shader_path = std::filesystem::path(asset_root) / "data" / shader_path;
100 }
101 if (!std::filesystem::exists(shader_path)) {
102 throw mxvk::Exception("postprocess: shader listed in shaders.txt was not found: " + shader_path.string());
103 }
104
105 mxvk::VK_Window::PostProcessingEffect effect{};
106 effect.fragmentShaderPath = shader_path.string();
107 effect.params = {0.0f, 1.0f, 1.0f, 0.0f};
108 effect.timeEnabled = true;
109 effects.push_back(effect);
110 }
111
112 return effects;
113 }
114
115 void attachEffects(const std::vector<mxvk::VK_Window::PostProcessingEffect> &effects) {
116 if (effects.empty()) {
118 return;
119 }
120
122 for (size_t i = 0; i < effects.size(); ++i) {
123 setPostProcessingShaderTimeEnabled(i, effects[i].timeEnabled);
124 }
126 std::cout << std::format("postprocess: attached {} post-processing effect(s)\n", effects.size());
127 }
128
129 void resizeCanvas(int width, int height) {
130 if (width <= 0 || height <= 0) {
131 return;
132 }
133 if (canvas != nullptr && canvas_width == width && canvas_height == height) {
134 return;
135 }
136
137 canvas.reset(SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGBA32));
138 if (canvas == nullptr) {
139 throw mxvk::Exception("postprocess: failed to create SDL surface: " + std::string(SDL_GetError()));
140 }
141 canvas_width = width;
142 canvas_height = height;
143 }
144
145 void drawCanvas() {
146 if (canvas == nullptr || !SDL_LockSurface(canvas.get())) {
147 return;
148 }
149
150 ++frame;
151 auto *pixels = static_cast<std::uint8_t *>(canvas->pixels);
152 for (int y = 0; y < canvas_height; ++y) {
153 std::uint8_t *row = pixels + y * canvas->pitch;
154 for (int x = 0; x < canvas_width; ++x) {
155 const float nx = static_cast<float>(x) / static_cast<float>(std::max(canvas_width - 1, 1));
156 const float ny = static_cast<float>(y) / static_cast<float>(std::max(canvas_height - 1, 1));
157 const int checker = ((x / 48) + (y / 48)) & 1;
158 const std::uint8_t red = static_cast<std::uint8_t>(40.0f + nx * 180.0f);
159 const std::uint8_t green = static_cast<std::uint8_t>(50.0f + ny * 160.0f);
160 const std::uint8_t blue = static_cast<std::uint8_t>(checker != 0 ? 220 : 80);
161 std::uint8_t *pixel = row + x * 4;
162 pixel[0] = static_cast<std::uint8_t>((red + frame) & 0xFF);
163 pixel[1] = green;
164 pixel[2] = blue;
165 pixel[3] = 255;
166 }
167 }
168
169 SDL_UnlockSurface(canvas.get());
170 }
171
172 std::string asset_root;
173 mxvk::VK_Sprite *sprite = nullptr;
174 std::unique_ptr<SDL_Surface, SurfaceDeleter> canvas;
175 int canvas_width = 1280;
176 int canvas_height = 720;
177 std::uint8_t frame = 0;
178 };
179} // namespace example
180
181int main(int argc, char **argv) {
182 try {
183 Arguments args = proc_args(argc, argv);
184 example::PostprocessWindow window(args.path, args.width, args.height, args.fullscreen, args.enable_vsync);
185 window.loop();
186 } catch (const mxvk::Exception &e) {
187 std::cerr << std::format("mxvk: Exception: {}\n", e.text());
188 return EXIT_FAILURE;
189 } catch (const ArgException<std::string> &e) {
190 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
191 return EXIT_FAILURE;
192 }
193 return EXIT_SUCCESS;
194}
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:872
Exception thrown by Argz::proc() on unrecognised or malformed options.
Definition argz.hpp:178
void event(SDL_Event &e) override
Handle one SDL event.
Definition post.cpp:41
void proc() override
Execute one processing/update step.
Definition post.cpp:50
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:37
VkExtent2D getSwapchainExtent() const noexcept
Get the current swapchain extent.
Definition mxvk.hpp:186
void loop()
Run the main event/render loop.
Definition mxvk.cpp:600
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:3477
std::vector< VK_Sprite * > attachPostProcessingShaders(const std::vector< PostProcessingEffect > &effects)
Definition mxvk.cpp:1159
void exit()
Request loop termination.
Definition mxvk.cpp:1126
VK_Window()=default
Construct an empty window object.
void setPostProcessingEnabled(bool enabled)
Definition mxvk.hpp:284
void setPostProcessingShaderTimeEnabled(bool enabled)
Keep shader param 1 updated with elapsed render time in seconds.
Definition mxvk.cpp:1248
int main(void)
Definition main.cpp:7
#define MXVK_VALIDATION
Definition mxvk.hpp:27
Utilities for loading and saving PNG images.
Definition mxvk.hpp:30
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:730
bool fullscreen
Whether fullscreen mode was requested.
Definition argz.hpp:736
bool enable_vsync
Enable FIFO present mode / v-sync (--enable-vsync).
Definition argz.hpp:750
int height
Viewport height in pixels (default: 720).
Definition argz.hpp:733
std::string path
Asset root; proc_args() defaults it to the executable directory.
Definition argz.hpp:735
int width
Viewport width in pixels (default: 1280).
Definition argz.hpp:732
void operator()(SDL_Surface *surface) const
Definition post.cpp:24
std::array< float, 4 > params
Definition mxvk.hpp:250