feat(gpu): suport Metal/MSL a macOS i shaders SPIR-V embedits
This commit is contained in:
+37
-34
@@ -133,44 +133,47 @@ add_custom_command(
|
||||
add_custom_target(resource_pack ALL DEPENDS ${RESOURCE_PACK})
|
||||
add_dependencies(${PROJECT_NAME} resource_pack)
|
||||
|
||||
# --- COMPILACIÓ DE SHADERS GLSL → SPIR-V ---
|
||||
# Compila tots els shaders .glsl a SPIR-V (Vulkan/Linux/Windows).
|
||||
# macOS necessitarà MSL en el futur (Metal) — es generen amb spirv-cross
|
||||
# o glslang amb target distint en una etapa posterior.
|
||||
# Sortida: build/shaders/*.spv
|
||||
# --- COMPILACIÓ DE SHADERS GLSL → SPIR-V (headers C++ embedits) ---
|
||||
# Compila els shaders .glsl a SPIR-V i els converteix en headers C++ embedits
|
||||
# (source/core/rendering/gpu/spv/*.h). Aquests headers es commiteen al repo:
|
||||
# en macOS no cal glslc (els headers ja existeixen). En Linux/Windows glslc
|
||||
# és obligatori per regenerar els headers en cada canvi del GLSL.
|
||||
#
|
||||
# Per a macOS hi ha a més els headers MSL escrits a mà a source/core/rendering/gpu/msl/.
|
||||
set(SHADERS_DIR "${CMAKE_SOURCE_DIR}/shaders")
|
||||
set(HEADERS_DIR "${CMAKE_SOURCE_DIR}/source/core/rendering/gpu/spv")
|
||||
set(ALL_SHADER_HEADERS
|
||||
"${HEADERS_DIR}/line_vert_spv.h"
|
||||
"${HEADERS_DIR}/line_frag_spv.h"
|
||||
"${HEADERS_DIR}/postfx_vert_spv.h"
|
||||
"${HEADERS_DIR}/postfx_frag_spv.h"
|
||||
)
|
||||
set(ALL_SHADER_SOURCES
|
||||
"${SHADERS_DIR}/line.vert.glsl"
|
||||
"${SHADERS_DIR}/line.frag.glsl"
|
||||
"${SHADERS_DIR}/postfx.vert.glsl"
|
||||
"${SHADERS_DIR}/postfx.frag.glsl"
|
||||
)
|
||||
find_program(GLSLC_EXE NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE})
|
||||
if(GLSLC_EXE)
|
||||
file(GLOB SHADER_SOURCES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/shaders/*.glsl")
|
||||
set(COMPILED_SHADERS "")
|
||||
foreach(SHADER ${SHADER_SOURCES})
|
||||
get_filename_component(SHADER_NAME ${SHADER} NAME)
|
||||
# Detectar stage del nom: line.vert.glsl → vert, line.frag.glsl → frag
|
||||
if(SHADER_NAME MATCHES "\\.vert\\.glsl$")
|
||||
set(SHADER_STAGE "vert")
|
||||
string(REPLACE ".glsl" ".spv" SPV_NAME ${SHADER_NAME})
|
||||
elseif(SHADER_NAME MATCHES "\\.frag\\.glsl$")
|
||||
set(SHADER_STAGE "frag")
|
||||
string(REPLACE ".glsl" ".spv" SPV_NAME ${SHADER_NAME})
|
||||
else()
|
||||
message(WARNING "Shader sense stage detectat: ${SHADER_NAME} (esperat .vert.glsl o .frag.glsl)")
|
||||
continue()
|
||||
endif()
|
||||
set(SPV_OUTPUT "${CMAKE_BINARY_DIR}/shaders/${SPV_NAME}")
|
||||
add_custom_command(
|
||||
OUTPUT ${SPV_OUTPUT}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/shaders"
|
||||
COMMAND ${GLSLC_EXE} -fshader-stage=${SHADER_STAGE} -O ${SHADER} -o ${SPV_OUTPUT}
|
||||
DEPENDS ${SHADER}
|
||||
COMMENT "Compilant shader ${SHADER_NAME} → ${SPV_NAME}"
|
||||
VERBATIM
|
||||
)
|
||||
list(APPEND COMPILED_SHADERS ${SPV_OUTPUT})
|
||||
endforeach()
|
||||
add_custom_target(shaders ALL DEPENDS ${COMPILED_SHADERS})
|
||||
add_custom_command(
|
||||
OUTPUT ${ALL_SHADER_HEADERS}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-D GLSLC=${GLSLC_EXE}
|
||||
-D SHADERS_DIR=${SHADERS_DIR}
|
||||
-D HEADERS_DIR=${HEADERS_DIR}
|
||||
-P ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
|
||||
DEPENDS ${ALL_SHADER_SOURCES} ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
|
||||
COMMENT "Compilant shaders GLSL → headers SPIR-V embedits"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
|
||||
add_dependencies(${PROJECT_NAME} shaders)
|
||||
message(STATUS "Shaders trobats: ${SHADER_SOURCES}")
|
||||
message(STATUS "Shaders: glslc trobat (${GLSLC_EXE}); headers SPV es regeneraran si canvia el GLSL")
|
||||
elseif(APPLE)
|
||||
message(STATUS "Shaders: glslc no trobat en macOS — s'usaran els headers SPV ja commiteats")
|
||||
else()
|
||||
message(FATAL_ERROR "glslc no trobat: instal·la 'shaderc' o 'vulkan-sdk' per compilar shaders SPIR-V")
|
||||
message(FATAL_ERROR "glslc no trobat: instal·la 'shaderc' o 'vulkan-sdk' per compilar shaders SPIR-V (obligatori a Linux/Windows)")
|
||||
endif()
|
||||
|
||||
# --- STATIC ANALYSIS / FORMAT TARGETS ---
|
||||
|
||||
@@ -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
@@ -0,0 +1,99 @@
|
||||
# compile_spirv.cmake
|
||||
# Compila shaders GLSL a SPIR-V i genera headers C++ embedibles.
|
||||
# Multiplataforma: Windows, macOS, Linux (no requereix bash ni xxd).
|
||||
#
|
||||
# Invocat per CMakeLists.txt amb:
|
||||
# cmake -D GLSLC=<path> -D SHADERS_DIR=<path> -D HEADERS_DIR=<path> -P compile_spirv.cmake
|
||||
#
|
||||
# També es pot executar manualment des de l'arrel del projecte:
|
||||
# cmake -D GLSLC=glslc -D SHADERS_DIR=shaders \
|
||||
# -D HEADERS_DIR=source/core/rendering/gpu/spv \
|
||||
# -P tools/shaders/compile_spirv.cmake
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_policy(SET CMP0007 NEW)
|
||||
|
||||
# Llista de shaders a compilar: font relativa a SHADERS_DIR
|
||||
set(SHADER_SOURCES
|
||||
"line.vert.glsl"
|
||||
"line.frag.glsl"
|
||||
"postfx.vert.glsl"
|
||||
"postfx.frag.glsl"
|
||||
)
|
||||
|
||||
# Nom de la variable C++ per a cada shader (mateix ordre).
|
||||
# UPPER_CASE perquè són constexpr globals (.clang-tidy ho exigeix).
|
||||
set(SHADER_VARS
|
||||
"LINE_VERT_SPV"
|
||||
"LINE_FRAG_SPV"
|
||||
"POSTFX_VERT_SPV"
|
||||
"POSTFX_FRAG_SPV"
|
||||
)
|
||||
|
||||
# Flags extra per a cada shader (necessaris perquè .vert.glsl/.frag.glsl no s'infereixen)
|
||||
set(SHADER_FLAGS
|
||||
"-fshader-stage=vert"
|
||||
"-fshader-stage=frag"
|
||||
"-fshader-stage=vert"
|
||||
"-fshader-stage=frag"
|
||||
)
|
||||
|
||||
list(LENGTH SHADER_SOURCES NUM_SHADERS)
|
||||
math(EXPR LAST_IDX "${NUM_SHADERS} - 1")
|
||||
|
||||
foreach(IDX RANGE ${LAST_IDX})
|
||||
list(GET SHADER_SOURCES ${IDX} SRC_NAME)
|
||||
list(GET SHADER_VARS ${IDX} VAR)
|
||||
list(GET SHADER_FLAGS ${IDX} EXTRA_FLAG)
|
||||
|
||||
# Derivem el nom del header a partir de la variable: LINE_VERT_SPV → line_vert_spv.h
|
||||
string(TOLOWER "${VAR}" HDR_BASE)
|
||||
set(SRC "${SHADERS_DIR}/${SRC_NAME}")
|
||||
set(SPV "${HEADERS_DIR}/${HDR_BASE}.spv")
|
||||
set(HDR "${HEADERS_DIR}/${HDR_BASE}.h")
|
||||
|
||||
message(STATUS "Compilant ${SRC} ...")
|
||||
|
||||
if(EXTRA_FLAG)
|
||||
execute_process(
|
||||
COMMAND "${GLSLC}" "${EXTRA_FLAG}" -O "${SRC}" -o "${SPV}"
|
||||
RESULT_VARIABLE GLSLC_RESULT
|
||||
ERROR_VARIABLE GLSLC_ERROR
|
||||
)
|
||||
else()
|
||||
execute_process(
|
||||
COMMAND "${GLSLC}" -O "${SRC}" -o "${SPV}"
|
||||
RESULT_VARIABLE GLSLC_RESULT
|
||||
ERROR_VARIABLE GLSLC_ERROR
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT GLSLC_RESULT EQUAL 0)
|
||||
message(FATAL_ERROR "glslc ha fallat per a ${SRC}:\n${GLSLC_ERROR}")
|
||||
endif()
|
||||
|
||||
# Llegim el binari SPV com a hex (sense separadors) i el dividim en bytes.
|
||||
file(READ "${SPV}" HEX_DATA HEX)
|
||||
string(REGEX MATCHALL ".." BYTES "${HEX_DATA}")
|
||||
list(LENGTH BYTES NUM_BYTES)
|
||||
|
||||
set(ARRAY_BODY "")
|
||||
foreach(BYTE ${BYTES})
|
||||
string(APPEND ARRAY_BODY " 0x${BYTE},\n")
|
||||
endforeach()
|
||||
|
||||
file(WRITE "${HDR}"
|
||||
"#pragma once\n"
|
||||
"#include <cstddef>\n"
|
||||
"#include <cstdint>\n"
|
||||
"static const uint8_t ${VAR}[] = {\n"
|
||||
"${ARRAY_BODY}"
|
||||
"};\n"
|
||||
"static const size_t ${VAR}_SIZE = ${NUM_BYTES};\n"
|
||||
)
|
||||
|
||||
file(REMOVE "${SPV}")
|
||||
message(STATUS " -> ${HDR} (${NUM_BYTES} bytes)")
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Shaders SPIR-V compilats correctament.")
|
||||
Reference in New Issue
Block a user