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);
36 attachEffects(loadEffects(asset_root +
"/data/shaders.txt"));
39 void event(SDL_Event &e)
override {
40 if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) {
43 if (e.type == SDL_EVENT_QUIT) {
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);
57 if (sprite !=
nullptr) {
58 sprite->updateTexture(canvas.get());
59 sprite->drawSpriteRect(0, 0, canvas_width, canvas_height);
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();
70 return std::string(first, last);
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);
79 std::vector<mxvk::VK_Window::PostProcessingEffect> effects;
81 while (std::getline(file, line)) {
82 const size_t comment = line.find(
'#');
83 if (comment != std::string::npos) {
91 std::filesystem::path shader_path(line);
92 if (shader_path.is_relative()) {
93 shader_path = std::filesystem::path(asset_root) /
"data" / shader_path;
95 if (!std::filesystem::exists(shader_path)) {
96 throw mxvk::Exception(
"postprocess: shader listed in shaders.txt was not found: " + shader_path.string());
99 mxvk::VK_Window::PostProcessingEffect effect{};
101 effect.
params = {0.0f, 1.0f, 1.0f, 0.0f};
103 effects.push_back(effect);
109 void attachEffects(
const std::vector<mxvk::VK_Window::PostProcessingEffect> &effects) {
110 if (effects.empty()) {
116 for (
size_t i = 0; i < effects.size(); ++i) {
120 std::cout << std::format(
"postprocess: attached {} post-processing effect(s)\n", effects.size());
123 void resizeCanvas(
int width,
int height) {
124 if (width <= 0 || height <= 0) {
127 if (canvas !=
nullptr && canvas_width == width && canvas_height == height) {
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()));
135 canvas_width = width;
136 canvas_height = height;
140 if (canvas ==
nullptr || !SDL_LockSurface(canvas.get())) {
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);
163 SDL_UnlockSurface(canvas.get());
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;
void event(SDL_Event &e) override
Handle one SDL event.
void proc() override
Execute one processing/update step.
PostprocessWindow(const std::string &path, int width, int height, bool fullscreen, bool enable_vsync)
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.
std::array< float, 4 > params
std::string fragmentShaderPath