diff --git a/data/shaders/upscale.frag b/data/shaders/upscale.frag new file mode 100644 index 0000000..fb9200d --- /dev/null +++ b/data/shaders/upscale.frag @@ -0,0 +1,15 @@ +#version 450 + +// Vulkan GLSL fragment shader — Nearest-neighbour upscale pass +// Used as the first render pass when supersampling is active. +// Compile: glslc upscale.frag -o upscale.frag.spv +// xxd -i upscale.frag.spv > ../../source/core/rendering/sdl3gpu/upscale_frag_spv.h + +layout(location = 0) in vec2 v_uv; +layout(location = 0) out vec4 out_color; + +layout(set = 2, binding = 0) uniform sampler2D scene; + +void main() { + out_color = texture(scene, v_uv); +} diff --git a/data/shaders/upscale.frag.spv b/data/shaders/upscale.frag.spv new file mode 100644 index 0000000..11c1f11 Binary files /dev/null and b/data/shaders/upscale.frag.spv differ diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp index 710478b..cb8e03c 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.cpp @@ -9,6 +9,7 @@ #ifndef __APPLE__ #include "core/rendering/sdl3gpu/postfx_frag_spv.h" #include "core/rendering/sdl3gpu/postfx_vert_spv.h" +#include "core/rendering/sdl3gpu/upscale_frag_spv.h" #endif #ifdef __APPLE__ @@ -176,6 +177,17 @@ fragment float4 postfx_fs(PostVOut in [[stage_in]], return float4(colour, 1.0f); } )"; +static const char* UPSCALE_FRAG_MSL = R"( +#include +using namespace metal; +struct VertOut { float4 pos [[position]]; float2 uv; }; +fragment float4 upscale_fs(VertOut in [[stage_in]], + texture2d scene [[texture(0)]], + sampler smp [[sampler(0)]]) +{ + return scene.sample(smp, in.uv); +} +)"; // NOLINTEND(readability-identifier-naming) #endif // __APPLE__ @@ -211,9 +223,7 @@ namespace Rendering { SDL_GetTextureSize(texture, &fw, &fh); game_width_ = static_cast(fw); game_height_ = static_cast(fh); - tex_width_ = game_width_ * oversample_; - tex_height_ = game_height_ * oversample_; - uniforms_.screen_height = static_cast(tex_height_); // Altura de la textura GPU + uniforms_.screen_height = static_cast(game_height_); uniforms_.oversample = static_cast(oversample_); // ---------------------------------------------------------------- @@ -245,15 +255,15 @@ namespace Rendering { } // ---------------------------------------------------------------- - // 3. Create scene texture (upload target + sampler source) + // 3. Create scene texture (upload target, always game resolution) // Format: B8G8R8A8_UNORM matches SDL ARGB8888 byte layout on LE // ---------------------------------------------------------------- SDL_GPUTextureCreateInfo tex_info = {}; tex_info.type = SDL_GPU_TEXTURETYPE_2D; tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; - tex_info.width = static_cast(tex_width_); - tex_info.height = static_cast(tex_height_); + tex_info.width = static_cast(game_width_); + tex_info.height = static_cast(game_height_); tex_info.layer_count_or_depth = 1; tex_info.num_levels = 1; scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info); @@ -264,11 +274,31 @@ namespace Rendering { } // ---------------------------------------------------------------- - // 4. Create upload transfer buffer (CPU → GPU, size = w*h*4 bytes) + // 3b. Create scaled texture (render target for upscale pass, only with SS) + // ---------------------------------------------------------------- + if (oversample_ > 1) { + SDL_GPUTextureCreateInfo scaled_info = {}; + scaled_info.type = SDL_GPU_TEXTURETYPE_2D; + scaled_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; + scaled_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + scaled_info.width = static_cast(game_width_ * oversample_); + scaled_info.height = static_cast(game_height_ * oversample_); + scaled_info.layer_count_or_depth = 1; + scaled_info.num_levels = 1; + scaled_texture_ = SDL_CreateGPUTexture(device_, &scaled_info); + if (scaled_texture_ == nullptr) { + SDL_Log("SDL3GPUShader: failed to create scaled texture: %s", SDL_GetError()); + cleanup(); + return false; + } + } + + // ---------------------------------------------------------------- + // 4. Create upload transfer buffer (CPU → GPU, always game resolution) // ---------------------------------------------------------------- SDL_GPUTransferBufferCreateInfo tb_info = {}; tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; - tb_info.size = static_cast(tex_width_ * tex_height_ * 4); + tb_info.size = static_cast(game_width_ * game_height_ * 4); upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info); if (upload_buffer_ == nullptr) { SDL_Log("SDL3GPUShader: failed to create upload buffer: %s", SDL_GetError()); @@ -316,7 +346,7 @@ namespace Rendering { } is_initialized_ = true; - SDL_Log("SDL3GPUShader: initialized OK (%dx%d)", tex_width_, tex_height_); + SDL_Log("SDL3GPUShader: initialized OK — game %dx%d, oversample %d", game_width_, game_height_, oversample_); return true; } @@ -326,6 +356,7 @@ namespace Rendering { auto SDL3GPUShader::createPipeline() -> bool { const SDL_GPUTextureFormat SWAPCHAIN_FMT = SDL_GetGPUSwapchainTextureFormat(device_, window_); + // ---- PostFX pipeline (scene/scaled → swapchain) ---- #ifdef __APPLE__ SDL_GPUShader* vert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0); SDL_GPUShader* frag = createShaderMSL(device_, POSTFX_FRAG_MSL, "postfx_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1); @@ -365,9 +396,48 @@ namespace Rendering { SDL_ReleaseGPUShader(device_, frag); if (pipeline_ == nullptr) { - SDL_Log("SDL3GPUShader: pipeline creation failed: %s", SDL_GetError()); + SDL_Log("SDL3GPUShader: PostFX pipeline creation failed: %s", SDL_GetError()); return false; } + + // ---- Upscale pipeline (scene → scaled_texture_, nearest) ---- +#ifdef __APPLE__ + SDL_GPUShader* uvert = createShaderMSL(device_, POSTFX_VERT_MSL, "postfx_vs", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0); + SDL_GPUShader* ufrag = createShaderMSL(device_, UPSCALE_FRAG_MSL, "upscale_fs", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0); +#else + SDL_GPUShader* uvert = createShaderSPIRV(device_, kpostfx_vert_spv, kpostfx_vert_spv_size, "main", SDL_GPU_SHADERSTAGE_VERTEX, 0, 0); + SDL_GPUShader* ufrag = createShaderSPIRV(device_, kupscale_frag_spv, kupscale_frag_spv_size, "main", SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 0); +#endif + + if ((uvert == nullptr) || (ufrag == nullptr)) { + SDL_Log("SDL3GPUShader: failed to compile upscale shaders"); + if (uvert != nullptr) { SDL_ReleaseGPUShader(device_, uvert); } + if (ufrag != nullptr) { SDL_ReleaseGPUShader(device_, ufrag); } + return false; + } + + SDL_GPUColorTargetDescription upscale_color_target = {}; + upscale_color_target.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; + upscale_color_target.blend_state = no_blend; + + SDL_GPUGraphicsPipelineCreateInfo upscale_pipe_info = {}; + upscale_pipe_info.vertex_shader = uvert; + upscale_pipe_info.fragment_shader = ufrag; + upscale_pipe_info.vertex_input_state = no_input; + upscale_pipe_info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST; + upscale_pipe_info.target_info.num_color_targets = 1; + upscale_pipe_info.target_info.color_target_descriptions = &upscale_color_target; + + upscale_pipeline_ = SDL_CreateGPUGraphicsPipeline(device_, &upscale_pipe_info); + + SDL_ReleaseGPUShader(device_, uvert); + SDL_ReleaseGPUShader(device_, ufrag); + + if (upscale_pipeline_ == nullptr) { + SDL_Log("SDL3GPUShader: upscale pipeline creation failed: %s", SDL_GetError()); + return false; + } + return true; } @@ -385,43 +455,8 @@ namespace Rendering { return; } - if (oversample_ <= 1) { - // Path sin supersampling: copia directa - std::memcpy(mapped, pixels, static_cast(width * height * 4)); - } else { - // Path con supersampling: expande cada pixel a OS×OS, oscurece última fila. - // Replica la fórmula del shader: mix(1.0, 0.42, scanline_strength). - auto* out = static_cast(mapped); - const int OS = oversample_; - const float BRIGHT_MUL = 1.0F; // rows 0..OS-2 (bright = 1.0×, sin amplificación) - const float DARK_MUL = 1.0F - (baked_scanline_strength_ * 0.58F); // row OS-1 - - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - const Uint32 SRC = pixels[(y * width) + x]; - const Uint32 ALPHA = (SRC >> 24) & 0xFFU; - const auto FR = static_cast((SRC >> 16) & 0xFFU); - const auto FG = static_cast((SRC >> 8) & 0xFFU); - const auto FB = static_cast(SRC & 0xFFU); - - auto make_px = [ALPHA](float rv, float gv, float bv) -> Uint32 { - auto cl = [](float v) -> Uint32 { return static_cast(std::min(255.0F, v)); }; - return (ALPHA << 24) | (cl(rv) << 16) | (cl(gv) << 8) | cl(bv); - }; - - const Uint32 BRIGHT = make_px(FR * BRIGHT_MUL, FG * BRIGHT_MUL, FB * BRIGHT_MUL); - const Uint32 DARK = make_px(FR * DARK_MUL, FG * DARK_MUL, FB * DARK_MUL); - - for (int dy = 0; dy < OS; ++dy) { - const Uint32 OUT_PX = (dy == OS - 1) ? DARK : BRIGHT; - const int DST_Y = (y * OS) + dy; - for (int dx = 0; dx < OS; ++dx) { - out[(DST_Y * (width * OS)) + ((x * OS) + dx)] = OUT_PX; - } - } - } - } - } + // Copia directa — el upscale lo hace la GPU en el primer render pass + std::memcpy(mapped, pixels, static_cast(width * height * 4)); SDL_UnmapGPUTransferBuffer(device_, upload_buffer_); } @@ -438,25 +473,44 @@ namespace Rendering { return; } - // ---- Copy pass: transfer buffer → scene texture ---- + // ---- Copy pass: transfer buffer → scene texture (siempre a resolución del juego) ---- SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd); if (copy != nullptr) { SDL_GPUTextureTransferInfo src = {}; src.transfer_buffer = upload_buffer_; src.offset = 0; - src.pixels_per_row = static_cast(tex_width_); - src.rows_per_layer = static_cast(tex_height_); + src.pixels_per_row = static_cast(game_width_); + src.rows_per_layer = static_cast(game_height_); SDL_GPUTextureRegion dst = {}; dst.texture = scene_texture_; - dst.w = static_cast(tex_width_); - dst.h = static_cast(tex_height_); + dst.w = static_cast(game_width_); + dst.h = static_cast(game_height_); dst.d = 1; SDL_UploadToGPUTexture(copy, &src, &dst, false); SDL_EndGPUCopyPass(copy); } + // ---- Upscale pass: scene_texture_ (game res) → scaled_texture_ (game × OS) ---- + if (oversample_ > 1 && scaled_texture_ != nullptr && upscale_pipeline_ != nullptr) { + SDL_GPUColorTargetInfo upscale_target = {}; + upscale_target.texture = scaled_texture_; + upscale_target.load_op = SDL_GPU_LOADOP_DONT_CARE; + upscale_target.store_op = SDL_GPU_STOREOP_STORE; + + SDL_GPURenderPass* upass = SDL_BeginGPURenderPass(cmd, &upscale_target, 1, nullptr); + if (upass != nullptr) { + SDL_BindGPUGraphicsPipeline(upass, upscale_pipeline_); + SDL_GPUTextureSamplerBinding ubinding = {}; + ubinding.texture = scene_texture_; + ubinding.sampler = sampler_; // NEAREST + SDL_BindGPUFragmentSamplers(upass, 0, &ubinding, 1); + SDL_DrawGPUPrimitives(upass, 3, 1, 0, 0); + SDL_EndGPURenderPass(upass); + } + } + // ---- Acquire swapchain texture ---- SDL_GPUTexture* swapchain = nullptr; Uint32 sw = 0; @@ -506,22 +560,28 @@ namespace Rendering { SDL_GPUViewport vp = {.x = vx, .y = vy, .w = vw, .h = vh, .min_depth = 0.0F, .max_depth = 1.0F}; SDL_SetGPUViewport(pass, &vp); - // pixel_scale: pixels físicos por pixel lógico de juego (para scanlines sin SS). - // Con SS las scanlines están horneadas en CPU → scanline_strength=0 → no se usa. - uniforms_.pixel_scale = (game_height_ > 0) - ? (vh / static_cast(game_height_)) - : 1.0F; + // pixel_scale: subpíxeles de textura por pixel lógico de juego. + // Sin SS: vh/game_height (zoom de ventana = subpíxeles por fila). + // Con SS: la textura entrada al PostFX tiene OS subfilas por fila lógica, + // así que pixel_scale = OS (independiente del zoom de ventana). + uniforms_.pixel_scale = (oversample_ > 1) + ? static_cast(oversample_) + : ((game_height_ > 0) ? (vh / static_cast(game_height_)) : 1.0F); uniforms_.time = static_cast(SDL_GetTicks()) / 1000.0F; uniforms_.oversample = static_cast(oversample_); - // Con supersampling usamos LINEAR para que el escalado a zooms no-múltiplo-de-3 - // promedia correctamente las filas de scanline horneadas en CPU. + // Con SS: leer de scaled_texture_ (ya ampliada); con LINEAR para suavizar + // el escalado final a zooms no-múltiplo-de-OS. + // Sin SS: leer de scene_texture_ con NEAREST. + SDL_GPUTexture* input_texture = (oversample_ > 1 && scaled_texture_ != nullptr) + ? scaled_texture_ + : scene_texture_; SDL_GPUSampler* active_sampler = (oversample_ > 1 && linear_sampler_ != nullptr) ? linear_sampler_ : sampler_; SDL_GPUTextureSamplerBinding binding = {}; - binding.texture = scene_texture_; + binding.texture = input_texture; binding.sampler = active_sampler; SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1); @@ -547,10 +607,18 @@ namespace Rendering { SDL_ReleaseGPUGraphicsPipeline(device_, pipeline_); pipeline_ = nullptr; } + if (upscale_pipeline_ != nullptr) { + SDL_ReleaseGPUGraphicsPipeline(device_, upscale_pipeline_); + upscale_pipeline_ = nullptr; + } if (scene_texture_ != nullptr) { SDL_ReleaseGPUTexture(device_, scene_texture_); scene_texture_ = nullptr; } + if (scaled_texture_ != nullptr) { + SDL_ReleaseGPUTexture(device_, scaled_texture_); + scaled_texture_ = nullptr; + } if (upload_buffer_ != nullptr) { SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_); upload_buffer_ = nullptr; @@ -638,10 +706,8 @@ namespace Rendering { uniforms_.bleeding = p.bleeding; uniforms_.flicker = p.flicker; - // Con supersampling las scanlines se hornean en CPU (uploadPixels). - // El shader recibe strength=0 para no aplicarlas de nuevo en GPU. - baked_scanline_strength_ = p.scanlines; - uniforms_.scanline_strength = (oversample_ > 1) ? 0.0F : p.scanlines; + // Las scanlines siempre las aplica el shader PostFX en GPU. + uniforms_.scanline_strength = p.scanlines; } void SDL3GPUShader::setVSync(bool vsync) { @@ -669,8 +735,8 @@ namespace Rendering { } // --------------------------------------------------------------------------- - // reinitTexturesAndBuffer — recrea scene_texture_ y upload_buffer_ con el - // tamaño actual (game × oversample_). No toca pipeline ni samplers. + // reinitTexturesAndBuffer — recrea scene_texture_, scaled_texture_ y + // upload_buffer_ con el factor oversample_ actual. No toca pipelines ni samplers. // --------------------------------------------------------------------------- auto SDL3GPUShader::reinitTexturesAndBuffer() -> bool { if (device_ == nullptr) { return false; } @@ -680,22 +746,25 @@ namespace Rendering { SDL_ReleaseGPUTexture(device_, scene_texture_); scene_texture_ = nullptr; } + if (scaled_texture_ != nullptr) { + SDL_ReleaseGPUTexture(device_, scaled_texture_); + scaled_texture_ = nullptr; + } if (upload_buffer_ != nullptr) { SDL_ReleaseGPUTransferBuffer(device_, upload_buffer_); upload_buffer_ = nullptr; } - tex_width_ = game_width_ * oversample_; - tex_height_ = game_height_ * oversample_; - uniforms_.screen_height = static_cast(tex_height_); + uniforms_.screen_height = static_cast(game_height_); uniforms_.oversample = static_cast(oversample_); + // scene_texture_: siempre a resolución del juego SDL_GPUTextureCreateInfo tex_info = {}; tex_info.type = SDL_GPU_TEXTURETYPE_2D; tex_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; - tex_info.width = static_cast(tex_width_); - tex_info.height = static_cast(tex_height_); + tex_info.width = static_cast(game_width_); + tex_info.height = static_cast(game_height_); tex_info.layer_count_or_depth = 1; tex_info.num_levels = 1; scene_texture_ = SDL_CreateGPUTexture(device_, &tex_info); @@ -704,18 +773,41 @@ namespace Rendering { return false; } + // scaled_texture_: solo con SS + if (oversample_ > 1) { + SDL_GPUTextureCreateInfo scaled_info = {}; + scaled_info.type = SDL_GPU_TEXTURETYPE_2D; + scaled_info.format = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; + scaled_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; + scaled_info.width = static_cast(game_width_ * oversample_); + scaled_info.height = static_cast(game_height_ * oversample_); + scaled_info.layer_count_or_depth = 1; + scaled_info.num_levels = 1; + scaled_texture_ = SDL_CreateGPUTexture(device_, &scaled_info); + if (scaled_texture_ == nullptr) { + SDL_Log("SDL3GPUShader: reinit — failed to create scaled texture: %s", SDL_GetError()); + SDL_ReleaseGPUTexture(device_, scene_texture_); + scene_texture_ = nullptr; + return false; + } + } + + // upload_buffer_: siempre a resolución del juego SDL_GPUTransferBufferCreateInfo tb_info = {}; tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; - tb_info.size = static_cast(tex_width_ * tex_height_ * 4); + tb_info.size = static_cast(game_width_ * game_height_ * 4); upload_buffer_ = SDL_CreateGPUTransferBuffer(device_, &tb_info); if (upload_buffer_ == nullptr) { SDL_Log("SDL3GPUShader: reinit — failed to create upload buffer: %s", SDL_GetError()); + if (scaled_texture_ != nullptr) { SDL_ReleaseGPUTexture(device_, scaled_texture_); scaled_texture_ = nullptr; } SDL_ReleaseGPUTexture(device_, scene_texture_); scene_texture_ = nullptr; return false; } - SDL_Log("SDL3GPUShader: oversample %d → texture %dx%d", oversample_, tex_width_, tex_height_); + SDL_Log("SDL3GPUShader: oversample %d — scene %dx%d, scaled %dx%d", + oversample_, game_width_, game_height_, + game_width_ * oversample_, game_height_ * oversample_); return true; } diff --git a/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp b/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp index 7878854..4d7e98d 100644 --- a/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp +++ b/source/core/rendering/sdl3gpu/sdl3gpu_shader.hpp @@ -84,20 +84,19 @@ namespace Rendering { SDL_Window* window_ = nullptr; SDL_GPUDevice* device_ = nullptr; - SDL_GPUGraphicsPipeline* pipeline_ = nullptr; - SDL_GPUTexture* scene_texture_ = nullptr; + SDL_GPUGraphicsPipeline* pipeline_ = nullptr; // PostFX pass + SDL_GPUGraphicsPipeline* upscale_pipeline_ = nullptr; // Upscale nearest pass (solo con SS) + SDL_GPUTexture* scene_texture_ = nullptr; // Canvas del juego (game_width_ × game_height_) + SDL_GPUTexture* scaled_texture_ = nullptr; // Render target intermedio (game × OS), solo con SS SDL_GPUTransferBuffer* upload_buffer_ = nullptr; - SDL_GPUSampler* sampler_ = nullptr; // NEAREST — para path sin supersampling - SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR — para path con supersampling + SDL_GPUSampler* sampler_ = nullptr; // NEAREST + SDL_GPUSampler* linear_sampler_ = nullptr; // LINEAR PostFXUniforms uniforms_{.vignette_strength = 0.6F, .chroma_strength = 0.15F, .scanline_strength = 0.7F, .screen_height = 192.0F, .pixel_scale = 1.0F, .oversample = 1.0F}; - int game_width_ = 0; // Dimensiones originales del canvas (sin SS) + int game_width_ = 0; // Dimensiones originales del canvas int game_height_ = 0; - int tex_width_ = 0; // Dimensiones de la textura GPU (game × oversample_) - int tex_height_ = 0; - int oversample_ = 1; // Factor SS actual (1 o 3) - float baked_scanline_strength_ = 0.0F; // Guardado para hornear en CPU + int oversample_ = 1; // Factor SS actual (1 o 3) bool is_initialized_ = false; bool vsync_ = true; bool integer_scale_ = false; diff --git a/source/core/rendering/sdl3gpu/upscale_frag_spv.h b/source/core/rendering/sdl3gpu/upscale_frag_spv.h new file mode 100644 index 0000000..9afa55e --- /dev/null +++ b/source/core/rendering/sdl3gpu/upscale_frag_spv.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include +static const uint8_t kupscale_frag_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x0d, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xc2, 0x01, 0x00, 0x00, 0x04, 0x00, 0x0a, 0x00, + 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, 0x45, 0x5f, 0x63, 0x70, + 0x70, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65, + 0x5f, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, + 0x04, 0x00, 0x08, 0x00, 0x47, 0x4c, 0x5f, 0x47, 0x4f, 0x4f, 0x47, 0x4c, + 0x45, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6f, 0x75, 0x74, 0x5f, + 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x73, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x76, 0x5f, 0x75, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00, +}; +static const size_t kupscale_frag_spv_size = 628;