feat(gpu): suport Metal/MSL a macOS i shaders SPIR-V embedits
This commit is contained in:
@@ -1,105 +1,58 @@
|
||||
// gpu_device.cpp - Implementación del wrapper de SDL_GPUDevice
|
||||
// gpu_device.cpp - Implementació del wrapper de SDL_GPUDevice
|
||||
|
||||
#include "core/rendering/gpu/gpu_device.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_gpu.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/utils/path_utils.hpp"
|
||||
|
||||
namespace Rendering::GPU {
|
||||
|
||||
GpuDevice::~GpuDevice() { destroy(); }
|
||||
GpuDevice::~GpuDevice() { destroy(); }
|
||||
|
||||
auto GpuDevice::init(SDL_Window* window) -> bool {
|
||||
window_ = window;
|
||||
auto GpuDevice::init(SDL_Window* window) -> bool {
|
||||
window_ = window;
|
||||
|
||||
// Solicitar backends en orden de preferencia: Vulkan (Linux/Windows),
|
||||
// Metal (macOS). Sin DirectX según decisión de proyecto.
|
||||
// SDL_GPU_SHADERFORMAT_SPIRV: shaders compilados con glslc.
|
||||
// SDL_GPU_SHADERFORMAT_MSL: pendiente para macOS (Fase futura).
|
||||
constexpr SDL_GPUShaderFormat SHADER_FORMATS =
|
||||
SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL;
|
||||
// Selecció del format de shader per plataforma:
|
||||
// - macOS → MSL (Metal Shading Language), shaders embedits a gpu/msl/*.msl.h
|
||||
// - Resta → SPIR-V (Vulkan), shaders embedits a gpu/spv/*.h
|
||||
#ifdef __APPLE__
|
||||
constexpr SDL_GPUShaderFormat SHADER_FORMATS = SDL_GPU_SHADERFORMAT_MSL;
|
||||
#else
|
||||
constexpr SDL_GPUShaderFormat SHADER_FORMATS = SDL_GPU_SHADERFORMAT_SPIRV;
|
||||
#endif
|
||||
|
||||
device_ = SDL_CreateGPUDevice(SHADER_FORMATS, /*debug=*/true, /*name=*/nullptr);
|
||||
if (device_ == nullptr) {
|
||||
std::cerr << "[GpuDevice] SDL_CreateGPUDevice falló: " << SDL_GetError() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* driver = SDL_GetGPUDeviceDriver(device_);
|
||||
std::cout << "[GpuDevice] Backend GPU: " << (driver != nullptr ? driver : "?") << '\n';
|
||||
|
||||
if (!SDL_ClaimWindowForGPUDevice(device_, window_)) {
|
||||
std::cerr << "[GpuDevice] SDL_ClaimWindowForGPUDevice falló: " << SDL_GetError() << '\n';
|
||||
SDL_DestroyGPUDevice(device_);
|
||||
device_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
swapchain_format_ = SDL_GetGPUSwapchainTextureFormat(device_, window_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpuDevice::destroy() {
|
||||
if (device_ != nullptr) {
|
||||
if (window_ != nullptr) {
|
||||
SDL_ReleaseWindowFromGPUDevice(device_, window_);
|
||||
device_ = SDL_CreateGPUDevice(SHADER_FORMATS, /*debug_mode=*/true, /*name=*/nullptr);
|
||||
if (device_ == nullptr) {
|
||||
std::cerr << "[GpuDevice] SDL_CreateGPUDevice falló: " << SDL_GetError() << '\n';
|
||||
return false;
|
||||
}
|
||||
SDL_DestroyGPUDevice(device_);
|
||||
device_ = nullptr;
|
||||
}
|
||||
window_ = nullptr;
|
||||
swapchain_format_ = SDL_GPU_TEXTUREFORMAT_INVALID;
|
||||
}
|
||||
|
||||
auto GpuDevice::loadShader(const std::string& spv_filename,
|
||||
SDL_GPUShaderStage stage,
|
||||
uint32_t num_uniform_buffers,
|
||||
uint32_t num_samplers) const -> SDL_GPUShader* {
|
||||
if (device_ == nullptr) {
|
||||
return nullptr;
|
||||
const char* driver = SDL_GetGPUDeviceDriver(device_);
|
||||
std::cout << "[GpuDevice] Backend GPU: " << (driver != nullptr ? driver : "?") << '\n';
|
||||
|
||||
if (!SDL_ClaimWindowForGPUDevice(device_, window_)) {
|
||||
std::cerr << "[GpuDevice] SDL_ClaimWindowForGPUDevice falló: " << SDL_GetError() << '\n';
|
||||
SDL_DestroyGPUDevice(device_);
|
||||
device_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
swapchain_format_ = SDL_GetGPUSwapchainTextureFormat(device_, window_);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Los .spv viven en build/shaders/ junto al binario.
|
||||
const std::string PATH = Utils::getExecutableDirectory() + "/shaders/" + spv_filename;
|
||||
|
||||
std::ifstream file(PATH, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "[GpuDevice] No s'ha pogut obrir el shader: " << PATH << '\n';
|
||||
return nullptr;
|
||||
void GpuDevice::destroy() {
|
||||
if (device_ != nullptr) {
|
||||
if (window_ != nullptr) {
|
||||
SDL_ReleaseWindowFromGPUDevice(device_, window_);
|
||||
}
|
||||
SDL_DestroyGPUDevice(device_);
|
||||
device_ = nullptr;
|
||||
}
|
||||
window_ = nullptr;
|
||||
swapchain_format_ = SDL_GPU_TEXTUREFORMAT_INVALID;
|
||||
}
|
||||
const std::streamsize SIZE = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
std::vector<uint8_t> buffer(static_cast<size_t>(SIZE));
|
||||
if (!file.read(reinterpret_cast<char*>(buffer.data()), SIZE)) {
|
||||
std::cerr << "[GpuDevice] Error llegint shader: " << PATH << '\n';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDL_GPUShaderCreateInfo info{};
|
||||
info.code = buffer.data();
|
||||
info.code_size = buffer.size();
|
||||
info.entrypoint = "main";
|
||||
info.format = SDL_GPU_SHADERFORMAT_SPIRV;
|
||||
info.stage = stage;
|
||||
info.num_uniform_buffers = num_uniform_buffers;
|
||||
info.num_samplers = num_samplers;
|
||||
info.num_storage_buffers = 0;
|
||||
info.num_storage_textures = 0;
|
||||
|
||||
SDL_GPUShader* shader = SDL_CreateGPUShader(device_, &info);
|
||||
if (shader == nullptr) {
|
||||
std::cerr << "[GpuDevice] SDL_CreateGPUShader (" << spv_filename << "): " << SDL_GetError() << '\n';
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
} // namespace Rendering::GPU
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
// gpu_device.hpp - Wrapper de SDL_GPUDevice
|
||||
// © 2026 JailDesigner
|
||||
//
|
||||
// Ownership del SDL_GPUDevice y del claim del window. Backend preferido:
|
||||
// Vulkan (Linux, Windows) y Metal (macOS). Sin DirectX.
|
||||
// Ownership del SDL_GPUDevice i del claim del window. Backend per plataforma:
|
||||
// Vulkan (Linux, Windows) i Metal (macOS). Sense DirectX.
|
||||
//
|
||||
// Uso:
|
||||
// Els shaders s'embedien al binari (no es carreguen de disc): SPIR-V via
|
||||
// headers generats per CMake a `gpu/spv/*.h`, i MSL a mà a `gpu/msl/*.msl.h`.
|
||||
// La creació dels SDL_GPUShader la fan els pipelines via shader_factory.hpp.
|
||||
//
|
||||
// Ús:
|
||||
// GpuDevice device;
|
||||
// if (!device.init(window)) return -1; // claim del window
|
||||
// ... renderer setup ...
|
||||
// ... pipelines setup ...
|
||||
// device.destroy(); // unclaim + destroy device
|
||||
|
||||
#pragma once
|
||||
@@ -15,45 +19,33 @@
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_gpu.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Rendering::GPU {
|
||||
|
||||
class GpuDevice {
|
||||
public:
|
||||
class GpuDevice {
|
||||
public:
|
||||
GpuDevice() = default;
|
||||
~GpuDevice();
|
||||
|
||||
// No copia / move (RAII propietario del device).
|
||||
// No copia / move (RAII propietari del device).
|
||||
GpuDevice(const GpuDevice&) = delete;
|
||||
auto operator=(const GpuDevice&) -> GpuDevice& = delete;
|
||||
GpuDevice(GpuDevice&&) = delete;
|
||||
auto operator=(GpuDevice&&) -> GpuDevice& = delete;
|
||||
|
||||
// Crea el device y claim el window. Devuelve false si no hay backend
|
||||
// soportado o si el driver no permite el claim.
|
||||
// Crea el device i claim el window. Selecciona MSL en macOS, SPIR-V
|
||||
// en la resta. Retorna false si no hi ha backend suportat o si el
|
||||
// driver no permet el claim.
|
||||
[[nodiscard]] auto init(SDL_Window* window) -> bool;
|
||||
void destroy();
|
||||
|
||||
// Carga un shader SPIR-V desde build/shaders/{name}.spv. Devuelve un
|
||||
// SDL_GPUShader* del que ahora es responsable el caller (libera con
|
||||
// SDL_ReleaseGPUShader). Retorna nullptr si falla.
|
||||
//
|
||||
// num_uniform_buffers: nº de uniform buffers que usa el shader (slot 0..N-1).
|
||||
// num_samplers: nº de samplers (combined image+sampler) usados por el shader.
|
||||
[[nodiscard]] auto loadShader(const std::string& spv_filename,
|
||||
SDL_GPUShaderStage stage,
|
||||
uint32_t num_uniform_buffers,
|
||||
uint32_t num_samplers = 0) const -> SDL_GPUShader*;
|
||||
|
||||
[[nodiscard]] auto get() const -> SDL_GPUDevice* { return device_; }
|
||||
[[nodiscard]] auto window() const -> SDL_Window* { return window_; }
|
||||
[[nodiscard]] auto swapchainFormat() const -> SDL_GPUTextureFormat { return swapchain_format_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
SDL_GPUDevice* device_{nullptr};
|
||||
SDL_Window* window_{nullptr};
|
||||
SDL_GPUTextureFormat swapchain_format_{SDL_GPU_TEXTUREFORMAT_INVALID};
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Rendering::GPU
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// gpu_line_pipeline.cpp - Implementación del pipeline de líneas
|
||||
// gpu_line_pipeline.cpp - Implementació del pipeline de línies
|
||||
|
||||
#include "core/rendering/gpu/gpu_line_pipeline.hpp"
|
||||
|
||||
@@ -8,6 +8,15 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "core/rendering/gpu/gpu_device.hpp"
|
||||
#include "core/rendering/gpu/shader_factory.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "core/rendering/gpu/msl/line_frag.msl.h"
|
||||
#include "core/rendering/gpu/msl/line_vert.msl.h"
|
||||
#else
|
||||
#include "core/rendering/gpu/spv/line_frag_spv.h"
|
||||
#include "core/rendering/gpu/spv/line_vert_spv.h"
|
||||
#endif
|
||||
|
||||
namespace Rendering::GPU {
|
||||
|
||||
@@ -20,12 +29,37 @@ namespace Rendering::GPU {
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GPUShader* vert = device.loadShader("line.vert.spv",
|
||||
// Vertex: 1 UBO (viewport_size). Fragment: cap recurs.
|
||||
#ifdef __APPLE__
|
||||
SDL_GPUShader* vert = createShaderMSL(owner_,
|
||||
Msl::LINE_VERT_MSL,
|
||||
"line_vs",
|
||||
SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
/*num_samplers=*/0,
|
||||
/*num_uniform_buffers=*/1);
|
||||
SDL_GPUShader* frag = device.loadShader("line.frag.spv",
|
||||
SDL_GPUShader* frag = createShaderMSL(owner_,
|
||||
Msl::LINE_FRAG_MSL,
|
||||
"line_fs",
|
||||
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||
/*num_samplers=*/0,
|
||||
/*num_uniform_buffers=*/0);
|
||||
#else
|
||||
SDL_GPUShader* vert = createShaderSPIRV(owner_,
|
||||
LINE_VERT_SPV,
|
||||
LINE_VERT_SPV_SIZE,
|
||||
"main",
|
||||
SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
/*num_samplers=*/0,
|
||||
/*num_uniform_buffers=*/1);
|
||||
SDL_GPUShader* frag = createShaderSPIRV(owner_,
|
||||
LINE_FRAG_SPV,
|
||||
LINE_FRAG_SPV_SIZE,
|
||||
"main",
|
||||
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||
/*num_samplers=*/0,
|
||||
/*num_uniform_buffers=*/0);
|
||||
#endif
|
||||
|
||||
if ((vert == nullptr) || (frag == nullptr)) {
|
||||
if (vert != nullptr) {
|
||||
SDL_ReleaseGPUShader(owner_, vert);
|
||||
@@ -33,7 +67,7 @@ namespace Rendering::GPU {
|
||||
if (frag != nullptr) {
|
||||
SDL_ReleaseGPUShader(owner_, frag);
|
||||
}
|
||||
std::cerr << "[GpuLinePipeline] Error cargando shaders\n";
|
||||
std::cerr << "[GpuLinePipeline] Error cargando shaders: " << SDL_GetError() << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -98,8 +132,8 @@ namespace Rendering::GPU {
|
||||
|
||||
pipeline_ = SDL_CreateGPUGraphicsPipeline(owner_, &info);
|
||||
|
||||
// Los shaders se pueden liberar tras crear el pipeline (SDL los retiene
|
||||
// internamente mientras el pipeline esté vivo).
|
||||
// Els shaders es poden alliberar després de crear el pipeline (SDL els
|
||||
// reté internament mentre el pipeline estigui viu).
|
||||
SDL_ReleaseGPUShader(owner_, vert);
|
||||
SDL_ReleaseGPUShader(owner_, frag);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// gpu_postfx_pipeline.cpp - Implementación del pipeline de postprocesado.
|
||||
// gpu_postfx_pipeline.cpp - Implementació del pipeline de postprocesat.
|
||||
|
||||
#include "core/rendering/gpu/gpu_postfx_pipeline.hpp"
|
||||
|
||||
@@ -8,91 +8,122 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "core/rendering/gpu/gpu_device.hpp"
|
||||
#include "core/rendering/gpu/shader_factory.hpp"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "core/rendering/gpu/msl/postfx_frag.msl.h"
|
||||
#include "core/rendering/gpu/msl/postfx_vert.msl.h"
|
||||
#else
|
||||
#include "core/rendering/gpu/spv/postfx_frag_spv.h"
|
||||
#include "core/rendering/gpu/spv/postfx_vert_spv.h"
|
||||
#endif
|
||||
|
||||
namespace Rendering::GPU {
|
||||
|
||||
GpuPostFxPipeline::~GpuPostFxPipeline() { destroy(); }
|
||||
GpuPostFxPipeline::~GpuPostFxPipeline() { destroy(); }
|
||||
|
||||
auto GpuPostFxPipeline::init(const GpuDevice& device,
|
||||
SDL_GPUTextureFormat target_format) -> bool {
|
||||
owner_ = device.get();
|
||||
if (owner_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// El vertex shader no usa UBO (emite tres vértices hardcodeados).
|
||||
// El fragment shader usa 1 sampler (escena) y 1 UBO (parámetros postpro).
|
||||
SDL_GPUShader* vert = device.loadShader("postfx.vert.spv",
|
||||
SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
/*num_uniform_buffers=*/0,
|
||||
/*num_samplers=*/0);
|
||||
SDL_GPUShader* frag = device.loadShader("postfx.frag.spv",
|
||||
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||
/*num_uniform_buffers=*/1,
|
||||
/*num_samplers=*/1);
|
||||
if ((vert == nullptr) || (frag == nullptr)) {
|
||||
if (vert != nullptr) {
|
||||
SDL_ReleaseGPUShader(owner_, vert);
|
||||
auto GpuPostFxPipeline::init(const GpuDevice& device,
|
||||
SDL_GPUTextureFormat target_format) -> bool {
|
||||
owner_ = device.get();
|
||||
if (owner_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (frag != nullptr) {
|
||||
SDL_ReleaseGPUShader(owner_, frag);
|
||||
|
||||
// Vertex shader: sense UBO ni vertex buffer (emet 3 vèrtexs hardcodejats).
|
||||
// Fragment shader: 1 sampler (escena) + 1 UBO (paràmetres postpro).
|
||||
#ifdef __APPLE__
|
||||
SDL_GPUShader* vert = createShaderMSL(owner_,
|
||||
Msl::POSTFX_VERT_MSL,
|
||||
"postfx_vs",
|
||||
SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
/*num_samplers=*/0,
|
||||
/*num_uniform_buffers=*/0);
|
||||
SDL_GPUShader* frag = createShaderMSL(owner_,
|
||||
Msl::POSTFX_FRAG_MSL,
|
||||
"postfx_fs",
|
||||
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||
/*num_samplers=*/1,
|
||||
/*num_uniform_buffers=*/1);
|
||||
#else
|
||||
SDL_GPUShader* vert = createShaderSPIRV(owner_,
|
||||
POSTFX_VERT_SPV,
|
||||
POSTFX_VERT_SPV_SIZE,
|
||||
"main",
|
||||
SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
/*num_samplers=*/0,
|
||||
/*num_uniform_buffers=*/0);
|
||||
SDL_GPUShader* frag = createShaderSPIRV(owner_,
|
||||
POSTFX_FRAG_SPV,
|
||||
POSTFX_FRAG_SPV_SIZE,
|
||||
"main",
|
||||
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||
/*num_samplers=*/1,
|
||||
/*num_uniform_buffers=*/1);
|
||||
#endif
|
||||
|
||||
if ((vert == nullptr) || (frag == nullptr)) {
|
||||
if (vert != nullptr) {
|
||||
SDL_ReleaseGPUShader(owner_, vert);
|
||||
}
|
||||
if (frag != nullptr) {
|
||||
SDL_ReleaseGPUShader(owner_, frag);
|
||||
}
|
||||
std::cerr << "[GpuPostFxPipeline] Error cargando shaders postfx: " << SDL_GetError() << '\n';
|
||||
return false;
|
||||
}
|
||||
std::cerr << "[GpuPostFxPipeline] Error cargando shaders postfx\n";
|
||||
return false;
|
||||
|
||||
// Sense vertex input: els tres vèrtexs del triangle es generen al shader.
|
||||
SDL_GPUVertexInputState vertex_input{};
|
||||
vertex_input.vertex_buffer_descriptions = nullptr;
|
||||
vertex_input.num_vertex_buffers = 0;
|
||||
vertex_input.vertex_attributes = nullptr;
|
||||
vertex_input.num_vertex_attributes = 0;
|
||||
|
||||
// Color target del postpro = swapchain. Sense blending: el postpro reescriu
|
||||
// píxels directament (la mescla amb l'escena ja es fa dins del shader).
|
||||
SDL_GPUColorTargetDescription color_target{};
|
||||
color_target.format = target_format;
|
||||
color_target.blend_state.enable_blend = false;
|
||||
color_target.blend_state.color_write_mask =
|
||||
SDL_GPU_COLORCOMPONENT_R | SDL_GPU_COLORCOMPONENT_G |
|
||||
SDL_GPU_COLORCOMPONENT_B | SDL_GPU_COLORCOMPONENT_A;
|
||||
|
||||
SDL_GPUGraphicsPipelineTargetInfo target_info{};
|
||||
target_info.color_target_descriptions = &color_target;
|
||||
target_info.num_color_targets = 1;
|
||||
target_info.has_depth_stencil_target = false;
|
||||
|
||||
SDL_GPUGraphicsPipelineCreateInfo info{};
|
||||
info.vertex_shader = vert;
|
||||
info.fragment_shader = frag;
|
||||
info.vertex_input_state = vertex_input;
|
||||
info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||
info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
|
||||
info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
|
||||
info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
|
||||
info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
|
||||
info.depth_stencil_state = {};
|
||||
info.target_info = target_info;
|
||||
|
||||
pipeline_ = SDL_CreateGPUGraphicsPipeline(owner_, &info);
|
||||
|
||||
SDL_ReleaseGPUShader(owner_, vert);
|
||||
SDL_ReleaseGPUShader(owner_, frag);
|
||||
|
||||
if (pipeline_ == nullptr) {
|
||||
std::cerr << "[GpuPostFxPipeline] SDL_CreateGPUGraphicsPipeline: "
|
||||
<< SDL_GetError() << '\n';
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sin vertex input: los tres vértices del triángulo se generan en el shader.
|
||||
SDL_GPUVertexInputState vertex_input{};
|
||||
vertex_input.vertex_buffer_descriptions = nullptr;
|
||||
vertex_input.num_vertex_buffers = 0;
|
||||
vertex_input.vertex_attributes = nullptr;
|
||||
vertex_input.num_vertex_attributes = 0;
|
||||
|
||||
// Color target del postpro = swapchain. Sin blending: el postpro reescribe
|
||||
// píxeles directamente (la mezcla con la escena ya se hizo dentro del shader).
|
||||
SDL_GPUColorTargetDescription color_target{};
|
||||
color_target.format = target_format;
|
||||
color_target.blend_state.enable_blend = false;
|
||||
color_target.blend_state.color_write_mask =
|
||||
SDL_GPU_COLORCOMPONENT_R | SDL_GPU_COLORCOMPONENT_G |
|
||||
SDL_GPU_COLORCOMPONENT_B | SDL_GPU_COLORCOMPONENT_A;
|
||||
|
||||
SDL_GPUGraphicsPipelineTargetInfo target_info{};
|
||||
target_info.color_target_descriptions = &color_target;
|
||||
target_info.num_color_targets = 1;
|
||||
target_info.has_depth_stencil_target = false;
|
||||
|
||||
SDL_GPUGraphicsPipelineCreateInfo info{};
|
||||
info.vertex_shader = vert;
|
||||
info.fragment_shader = frag;
|
||||
info.vertex_input_state = vertex_input;
|
||||
info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
|
||||
info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
|
||||
info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
|
||||
info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_COUNTER_CLOCKWISE;
|
||||
info.multisample_state.sample_count = SDL_GPU_SAMPLECOUNT_1;
|
||||
info.depth_stencil_state = {};
|
||||
info.target_info = target_info;
|
||||
|
||||
pipeline_ = SDL_CreateGPUGraphicsPipeline(owner_, &info);
|
||||
|
||||
SDL_ReleaseGPUShader(owner_, vert);
|
||||
SDL_ReleaseGPUShader(owner_, frag);
|
||||
|
||||
if (pipeline_ == nullptr) {
|
||||
std::cerr << "[GpuPostFxPipeline] SDL_CreateGPUGraphicsPipeline: "
|
||||
<< SDL_GetError() << '\n';
|
||||
return false;
|
||||
void GpuPostFxPipeline::destroy() {
|
||||
if ((pipeline_ != nullptr) && (owner_ != nullptr)) {
|
||||
SDL_ReleaseGPUGraphicsPipeline(owner_, pipeline_);
|
||||
}
|
||||
pipeline_ = nullptr;
|
||||
owner_ = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpuPostFxPipeline::destroy() {
|
||||
if ((pipeline_ != nullptr) && (owner_ != nullptr)) {
|
||||
SDL_ReleaseGPUGraphicsPipeline(owner_, pipeline_);
|
||||
}
|
||||
pipeline_ = nullptr;
|
||||
owner_ = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Rendering::GPU
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// line_frag.msl.h - Metal Shading Language del fragment shader de línies
|
||||
// © 2026 JailDesigner
|
||||
//
|
||||
// IMPORTANT: mantenir sincronitzat a mà amb shaders/line.frag.glsl. SDL3 GPU
|
||||
// compila aquest string MSL en runtime; no hi ha generador automàtic.
|
||||
//
|
||||
// Aplica antialias geomètric via smoothstep sobre edge_dist (±1 als laterals,
|
||||
// 0 al centre del quad). Sense uniforms ni samplers.
|
||||
|
||||
#pragma once
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace Rendering::GPU::Msl {
|
||||
|
||||
inline constexpr const char* LINE_FRAG_MSL = R"(
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct VOut {
|
||||
float4 pos [[position]];
|
||||
float4 color;
|
||||
float edge_dist;
|
||||
};
|
||||
|
||||
fragment float4 line_fs(VOut in [[stage_in]]) {
|
||||
float d = abs(in.edge_dist);
|
||||
float alpha = 1.0 - smoothstep(0.7, 1.0, d);
|
||||
return float4(in.color.rgb, in.color.a * alpha);
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace Rendering::GPU::Msl
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -0,0 +1,54 @@
|
||||
// line_vert.msl.h - Metal Shading Language del vertex shader de línies
|
||||
// © 2026 JailDesigner
|
||||
//
|
||||
// IMPORTANT: mantenir sincronitzat a mà amb shaders/line.vert.glsl. SDL3 GPU
|
||||
// compila aquest string MSL en runtime; no hi ha generador automàtic. Qualsevol
|
||||
// canvi al UBO o al layout de vèrtex cal replicar-lo ací al mateix commit.
|
||||
//
|
||||
// Mapping SDL3 GPU → Metal (segons src/gpu/metal/SDL_gpu_metal.m upstream):
|
||||
// - Vertex buffers d'usuari: [[buffer(14 + slot)]] (METAL_FIRST_VERTEX_BUFFER_SLOT=14).
|
||||
// A través de [[stage_in]] amb [[attribute(N)]], Metal resol automàticament
|
||||
// a partir del MTLVertexDescriptor del pipeline state (transparent al MSL).
|
||||
// - Vertex UBO slot 0 SDL → [[buffer(0)]] MSL (sense offset).
|
||||
|
||||
#pragma once
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace Rendering::GPU::Msl {
|
||||
|
||||
inline constexpr const char* LINE_VERT_MSL = R"(
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct VIn {
|
||||
float2 in_position [[attribute(0)]];
|
||||
float4 in_color [[attribute(1)]];
|
||||
float in_edge_dist[[attribute(2)]];
|
||||
};
|
||||
|
||||
struct VOut {
|
||||
float4 pos [[position]];
|
||||
float4 color;
|
||||
float edge_dist;
|
||||
};
|
||||
|
||||
struct LineUBO {
|
||||
float2 viewport_size;
|
||||
float2 _padding;
|
||||
};
|
||||
|
||||
vertex VOut line_vs(VIn in [[stage_in]],
|
||||
constant LineUBO& ubo [[buffer(0)]]) {
|
||||
float2 ndc = (in.in_position / ubo.viewport_size) * 2.0 - 1.0;
|
||||
ndc.y = -ndc.y;
|
||||
VOut out;
|
||||
out.pos = float4(ndc, 0.0, 1.0);
|
||||
out.color = in.in_color;
|
||||
out.edge_dist = in.in_edge_dist;
|
||||
return out;
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace Rendering::GPU::Msl
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -0,0 +1,90 @@
|
||||
// postfx_frag.msl.h - Metal Shading Language del fragment shader del postpro
|
||||
// © 2026 JailDesigner
|
||||
//
|
||||
// IMPORTANT: mantenir sincronitzat a mà amb shaders/postfx.frag.glsl. SDL3 GPU
|
||||
// compila aquest string MSL en runtime; no hi ha generador automàtic. Qualsevol
|
||||
// canvi al struct PostFxUniforms (gpu_postfx_pipeline.hpp), al GLSL o al MSL
|
||||
// cal replicar-lo a totes tres al mateix commit.
|
||||
//
|
||||
// Composició final: bloom 5×5 amb high-pass, flicker sinusoidal global,
|
||||
// background pulse sumat. Recursos:
|
||||
// - texture2d<float> scene [[texture(0)]] + sampler [[sampler(0)]]
|
||||
// - constant PostFxUBO& ubo [[buffer(0)]] (slot 0 SDL → buffer(0) MSL)
|
||||
//
|
||||
// L'struct PostFxUBO té layout idèntic a PostFxUniforms (5×vec4 = 80 bytes).
|
||||
|
||||
#pragma once
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace Rendering::GPU::Msl {
|
||||
|
||||
inline constexpr const char* POSTFX_FRAG_MSL = R"(
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct PostVOut {
|
||||
float4 pos [[position]];
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
struct PostFxUBO {
|
||||
float time;
|
||||
float bloom_intensity;
|
||||
float bloom_threshold;
|
||||
float bloom_radius_px;
|
||||
|
||||
float flicker_amplitude;
|
||||
float flicker_frequency_hz;
|
||||
float background_pulse_freq_hz;
|
||||
float pad_a;
|
||||
|
||||
float4 background_min;
|
||||
float4 background_max;
|
||||
|
||||
float2 texel_size;
|
||||
float2 pad_b;
|
||||
};
|
||||
|
||||
constant float TAU = 6.28318530718;
|
||||
|
||||
fragment float4 postfx_fs(PostVOut in [[stage_in]],
|
||||
texture2d<float> scene [[texture(0)]],
|
||||
sampler samp [[sampler(0)]],
|
||||
constant PostFxUBO& ubo [[buffer(0)]]) {
|
||||
// === BLOOM ===
|
||||
float3 src = scene.sample(samp, in.uv).rgb;
|
||||
float3 bloom = float3(0.0);
|
||||
float total_weight = 0.0;
|
||||
for (int dy = -2; dy <= 2; ++dy) {
|
||||
for (int dx = -2; dx <= 2; ++dx) {
|
||||
float2 offset = float2(float(dx), float(dy)) * ubo.texel_size * ubo.bloom_radius_px;
|
||||
float3 c = scene.sample(samp, in.uv + offset).rgb;
|
||||
float luma = max(c.r, max(c.g, c.b));
|
||||
float high_pass = max(0.0, luma - ubo.bloom_threshold);
|
||||
float w = exp(-float(dx * dx + dy * dy) / 4.0);
|
||||
bloom += c * high_pass * w;
|
||||
total_weight += w;
|
||||
}
|
||||
}
|
||||
if (total_weight > 0.0) {
|
||||
bloom /= total_weight;
|
||||
}
|
||||
bloom *= ubo.bloom_intensity;
|
||||
|
||||
// === FLICKER ===
|
||||
float pulse = (sin(ubo.time * ubo.flicker_frequency_hz * TAU) * 0.5) + 0.5;
|
||||
float flicker = 1.0 - (ubo.flicker_amplitude * (1.0 - pulse));
|
||||
|
||||
// === BACKGROUND PULSE ===
|
||||
float bg_pulse = (sin(ubo.time * ubo.background_pulse_freq_hz * TAU) * 0.5) + 0.5;
|
||||
float3 background = mix(ubo.background_min.rgb, ubo.background_max.rgb, bg_pulse);
|
||||
|
||||
// === COMPOSICIÓ ===
|
||||
float3 lines_and_glow = (src + bloom) * flicker;
|
||||
return float4(background + lines_and_glow, 1.0);
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace Rendering::GPU::Msl
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -0,0 +1,38 @@
|
||||
// postfx_vert.msl.h - Metal Shading Language del vertex shader del postpro
|
||||
// © 2026 JailDesigner
|
||||
//
|
||||
// IMPORTANT: mantenir sincronitzat a mà amb shaders/postfx.vert.glsl. SDL3 GPU
|
||||
// compila aquest string MSL en runtime; no hi ha generador automàtic.
|
||||
//
|
||||
// Fullscreen triangle: 3 vèrtexs en (-1,-1), (3,-1), (-1,3) generats per
|
||||
// [[vertex_id]]. UV.y invertida per compensar la diferència de convenció
|
||||
// entre clip-space del line shader (Y-flip) i el mostreig SDL_gpu (origen
|
||||
// top-left). Sense uniforms ni vertex buffers (DrawPrimitives vertex_count=3).
|
||||
|
||||
#pragma once
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace Rendering::GPU::Msl {
|
||||
|
||||
inline constexpr const char* POSTFX_VERT_MSL = R"(
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct PostVOut {
|
||||
float4 pos [[position]];
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
vertex PostVOut postfx_vs(uint vid [[vertex_id]]) {
|
||||
const float2 positions[3] = { {-1.0, -1.0}, { 3.0, -1.0}, {-1.0, 3.0} };
|
||||
const float2 uvs[3] = { { 0.0, 1.0}, { 2.0, 1.0}, { 0.0, -1.0} };
|
||||
PostVOut out;
|
||||
out.pos = float4(positions[vid], 0.0, 1.0);
|
||||
out.uv = uvs[vid];
|
||||
return out;
|
||||
}
|
||||
)";
|
||||
|
||||
} // namespace Rendering::GPU::Msl
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -0,0 +1,60 @@
|
||||
// shader_factory.hpp - Helpers per crear SDL_GPUShader segons plataforma
|
||||
// © 2026 JailDesigner
|
||||
//
|
||||
// En __APPLE__ s'utilitza MSL (Metal Shading Language) embedit com a string
|
||||
// literal C++. En la resta de plataformes (Linux/Windows) s'utilitza SPIR-V
|
||||
// embedit com a uint8_t[] en headers generats per CMake. La selecció és
|
||||
// compile-time via #ifdef.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_gpu.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace Rendering::GPU {
|
||||
|
||||
#ifdef __APPLE__
|
||||
inline auto createShaderMSL(SDL_GPUDevice* device,
|
||||
const char* msl_source,
|
||||
const char* entrypoint,
|
||||
SDL_GPUShaderStage stage,
|
||||
Uint32 num_samplers,
|
||||
Uint32 num_uniform_buffers) -> SDL_GPUShader* {
|
||||
SDL_GPUShaderCreateInfo info{};
|
||||
info.code = reinterpret_cast<const Uint8*>(msl_source);
|
||||
info.code_size = std::strlen(msl_source) + 1;
|
||||
info.entrypoint = entrypoint;
|
||||
info.format = SDL_GPU_SHADERFORMAT_MSL;
|
||||
info.stage = stage;
|
||||
info.num_samplers = num_samplers;
|
||||
info.num_uniform_buffers = num_uniform_buffers;
|
||||
info.num_storage_buffers = 0;
|
||||
info.num_storage_textures = 0;
|
||||
return SDL_CreateGPUShader(device, &info);
|
||||
}
|
||||
#else
|
||||
inline auto createShaderSPIRV(SDL_GPUDevice* device,
|
||||
const std::uint8_t* spv_code,
|
||||
std::size_t spv_size,
|
||||
const char* entrypoint,
|
||||
SDL_GPUShaderStage stage,
|
||||
Uint32 num_samplers,
|
||||
Uint32 num_uniform_buffers) -> SDL_GPUShader* {
|
||||
SDL_GPUShaderCreateInfo info{};
|
||||
info.code = spv_code;
|
||||
info.code_size = spv_size;
|
||||
info.entrypoint = entrypoint;
|
||||
info.format = SDL_GPU_SHADERFORMAT_SPIRV;
|
||||
info.stage = stage;
|
||||
info.num_samplers = num_samplers;
|
||||
info.num_uniform_buffers = num_uniform_buffers;
|
||||
info.num_storage_buffers = 0;
|
||||
info.num_storage_textures = 0;
|
||||
return SDL_CreateGPUShader(device, &info);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Rendering::GPU
|
||||
@@ -0,0 +1,670 @@
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
static const uint8_t LINE_FRAG_SPV[] = {
|
||||
0x03,
|
||||
0x02,
|
||||
0x23,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x0d,
|
||||
0x00,
|
||||
0x25,
|
||||
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,
|
||||
0x08,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x6d,
|
||||
0x61,
|
||||
0x69,
|
||||
0x6e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x15,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x10,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x15,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x47,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x17,
|
||||
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,
|
||||
0x20,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x2b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x80,
|
||||
0x3f,
|
||||
0x2b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0f,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x33,
|
||||
0x33,
|
||||
0x33,
|
||||
0x3f,
|
||||
0x17,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x14,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x14,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x15,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x16,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x16,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x15,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x1b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x2b,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x1b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1c,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x03,
|
||||
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,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0a,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0c,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0c,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0b,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0c,
|
||||
0x00,
|
||||
0x08,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x31,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0f,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0c,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x83,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x12,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x0e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x11,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3d,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x19,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x41,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x09,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x17,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1c,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3d,
|
||||
0x00,
|
||||
0x04,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1d,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x85,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x1e,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x12,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x51,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x21,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x19,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x51,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x22,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x19,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x51,
|
||||
0x00,
|
||||
0x05,
|
||||
0x00,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x23,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x19,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x02,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x50,
|
||||
0x00,
|
||||
0x07,
|
||||
0x00,
|
||||
0x13,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x24,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x21,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x22,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x23,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x3e,
|
||||
0x00,
|
||||
0x03,
|
||||
0x00,
|
||||
0x15,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x24,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0xfd,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
0x38,
|
||||
0x00,
|
||||
0x01,
|
||||
0x00,
|
||||
};
|
||||
static const size_t LINE_FRAG_SPV_SIZE = 664;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user