Merge branch 'feat/metal-msl-support': suport Metal/MSL a macOS
This commit is contained in:
+37
-34
@@ -133,44 +133,47 @@ add_custom_command(
|
|||||||
add_custom_target(resource_pack ALL DEPENDS ${RESOURCE_PACK})
|
add_custom_target(resource_pack ALL DEPENDS ${RESOURCE_PACK})
|
||||||
add_dependencies(${PROJECT_NAME} resource_pack)
|
add_dependencies(${PROJECT_NAME} resource_pack)
|
||||||
|
|
||||||
# --- COMPILACIÓ DE SHADERS GLSL → SPIR-V ---
|
# --- COMPILACIÓ DE SHADERS GLSL → SPIR-V (headers C++ embedits) ---
|
||||||
# Compila tots els shaders .glsl a SPIR-V (Vulkan/Linux/Windows).
|
# Compila els shaders .glsl a SPIR-V i els converteix en headers C++ embedits
|
||||||
# macOS necessitarà MSL en el futur (Metal) — es generen amb spirv-cross
|
# (source/core/rendering/gpu/spv/*.h). Aquests headers es commiteen al repo:
|
||||||
# o glslang amb target distint en una etapa posterior.
|
# en macOS no cal glslc (els headers ja existeixen). En Linux/Windows glslc
|
||||||
# Sortida: build/shaders/*.spv
|
# é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})
|
find_program(GLSLC_EXE NAMES glslc HINTS ${Vulkan_GLSLC_EXECUTABLE})
|
||||||
if(GLSLC_EXE)
|
if(GLSLC_EXE)
|
||||||
file(GLOB SHADER_SOURCES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/shaders/*.glsl")
|
add_custom_command(
|
||||||
set(COMPILED_SHADERS "")
|
OUTPUT ${ALL_SHADER_HEADERS}
|
||||||
foreach(SHADER ${SHADER_SOURCES})
|
COMMAND ${CMAKE_COMMAND}
|
||||||
get_filename_component(SHADER_NAME ${SHADER} NAME)
|
-D GLSLC=${GLSLC_EXE}
|
||||||
# Detectar stage del nom: line.vert.glsl → vert, line.frag.glsl → frag
|
-D SHADERS_DIR=${SHADERS_DIR}
|
||||||
if(SHADER_NAME MATCHES "\\.vert\\.glsl$")
|
-D HEADERS_DIR=${HEADERS_DIR}
|
||||||
set(SHADER_STAGE "vert")
|
-P ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
|
||||||
string(REPLACE ".glsl" ".spv" SPV_NAME ${SHADER_NAME})
|
DEPENDS ${ALL_SHADER_SOURCES} ${CMAKE_SOURCE_DIR}/tools/shaders/compile_spirv.cmake
|
||||||
elseif(SHADER_NAME MATCHES "\\.frag\\.glsl$")
|
COMMENT "Compilant shaders GLSL → headers SPIR-V embedits"
|
||||||
set(SHADER_STAGE "frag")
|
VERBATIM
|
||||||
string(REPLACE ".glsl" ".spv" SPV_NAME ${SHADER_NAME})
|
)
|
||||||
else()
|
add_custom_target(shaders DEPENDS ${ALL_SHADER_HEADERS})
|
||||||
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_dependencies(${PROJECT_NAME} shaders)
|
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()
|
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()
|
endif()
|
||||||
|
|
||||||
# --- STATIC ANALYSIS / FORMAT TARGETS ---
|
# --- 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 "core/rendering/gpu/gpu_device.hpp"
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_gpu.h>
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "core/utils/path_utils.hpp"
|
|
||||||
|
|
||||||
namespace Rendering::GPU {
|
namespace Rendering::GPU {
|
||||||
|
|
||||||
GpuDevice::~GpuDevice() { destroy(); }
|
GpuDevice::~GpuDevice() { destroy(); }
|
||||||
|
|
||||||
auto GpuDevice::init(SDL_Window* window) -> bool {
|
auto GpuDevice::init(SDL_Window* window) -> bool {
|
||||||
window_ = window;
|
window_ = window;
|
||||||
|
|
||||||
// Solicitar backends en orden de preferencia: Vulkan (Linux/Windows),
|
// Selecció del format de shader per plataforma:
|
||||||
// Metal (macOS). Sin DirectX según decisión de proyecto.
|
// - macOS → MSL (Metal Shading Language), shaders embedits a gpu/msl/*.msl.h
|
||||||
// SDL_GPU_SHADERFORMAT_SPIRV: shaders compilados con glslc.
|
// - Resta → SPIR-V (Vulkan), shaders embedits a gpu/spv/*.h
|
||||||
// SDL_GPU_SHADERFORMAT_MSL: pendiente para macOS (Fase futura).
|
#ifdef __APPLE__
|
||||||
constexpr SDL_GPUShaderFormat SHADER_FORMATS =
|
constexpr SDL_GPUShaderFormat SHADER_FORMATS = SDL_GPU_SHADERFORMAT_MSL;
|
||||||
SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL;
|
#else
|
||||||
|
constexpr SDL_GPUShaderFormat SHADER_FORMATS = SDL_GPU_SHADERFORMAT_SPIRV;
|
||||||
|
#endif
|
||||||
|
|
||||||
device_ = SDL_CreateGPUDevice(SHADER_FORMATS, /*debug=*/true, /*name=*/nullptr);
|
device_ = SDL_CreateGPUDevice(SHADER_FORMATS, /*debug_mode=*/true, /*name=*/nullptr);
|
||||||
if (device_ == nullptr) {
|
if (device_ == nullptr) {
|
||||||
std::cerr << "[GpuDevice] SDL_CreateGPUDevice falló: " << SDL_GetError() << '\n';
|
std::cerr << "[GpuDevice] SDL_CreateGPUDevice falló: " << SDL_GetError() << '\n';
|
||||||
return false;
|
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_);
|
|
||||||
}
|
}
|
||||||
SDL_DestroyGPUDevice(device_);
|
|
||||||
device_ = nullptr;
|
|
||||||
}
|
|
||||||
window_ = nullptr;
|
|
||||||
swapchain_format_ = SDL_GPU_TEXTUREFORMAT_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto GpuDevice::loadShader(const std::string& spv_filename,
|
const char* driver = SDL_GetGPUDeviceDriver(device_);
|
||||||
SDL_GPUShaderStage stage,
|
std::cout << "[GpuDevice] Backend GPU: " << (driver != nullptr ? driver : "?") << '\n';
|
||||||
uint32_t num_uniform_buffers,
|
|
||||||
uint32_t num_samplers) const -> SDL_GPUShader* {
|
if (!SDL_ClaimWindowForGPUDevice(device_, window_)) {
|
||||||
if (device_ == nullptr) {
|
std::cerr << "[GpuDevice] SDL_ClaimWindowForGPUDevice falló: " << SDL_GetError() << '\n';
|
||||||
return nullptr;
|
SDL_DestroyGPUDevice(device_);
|
||||||
|
device_ = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
swapchain_format_ = SDL_GetGPUSwapchainTextureFormat(device_, window_);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Los .spv viven en build/shaders/ junto al binario.
|
void GpuDevice::destroy() {
|
||||||
const std::string PATH = Utils::getExecutableDirectory() + "/shaders/" + spv_filename;
|
if (device_ != nullptr) {
|
||||||
|
if (window_ != nullptr) {
|
||||||
std::ifstream file(PATH, std::ios::binary | std::ios::ate);
|
SDL_ReleaseWindowFromGPUDevice(device_, window_);
|
||||||
if (!file.is_open()) {
|
}
|
||||||
std::cerr << "[GpuDevice] No s'ha pogut obrir el shader: " << PATH << '\n';
|
SDL_DestroyGPUDevice(device_);
|
||||||
return nullptr;
|
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
|
} // namespace Rendering::GPU
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
// gpu_device.hpp - Wrapper de SDL_GPUDevice
|
// gpu_device.hpp - Wrapper de SDL_GPUDevice
|
||||||
// © 2026 JailDesigner
|
// © 2026 JailDesigner
|
||||||
//
|
//
|
||||||
// Ownership del SDL_GPUDevice y del claim del window. Backend preferido:
|
// Ownership del SDL_GPUDevice i del claim del window. Backend per plataforma:
|
||||||
// Vulkan (Linux, Windows) y Metal (macOS). Sin DirectX.
|
// 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;
|
// GpuDevice device;
|
||||||
// if (!device.init(window)) return -1; // claim del window
|
// if (!device.init(window)) return -1; // claim del window
|
||||||
// ... renderer setup ...
|
// ... pipelines setup ...
|
||||||
// device.destroy(); // unclaim + destroy device
|
// device.destroy(); // unclaim + destroy device
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -15,45 +19,33 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_gpu.h>
|
#include <SDL3/SDL_gpu.h>
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace Rendering::GPU {
|
namespace Rendering::GPU {
|
||||||
|
|
||||||
class GpuDevice {
|
class GpuDevice {
|
||||||
public:
|
public:
|
||||||
GpuDevice() = default;
|
GpuDevice() = default;
|
||||||
~GpuDevice();
|
~GpuDevice();
|
||||||
|
|
||||||
// No copia / move (RAII propietario del device).
|
// No copia / move (RAII propietari del device).
|
||||||
GpuDevice(const GpuDevice&) = delete;
|
GpuDevice(const GpuDevice&) = delete;
|
||||||
auto operator=(const GpuDevice&) -> GpuDevice& = delete;
|
auto operator=(const GpuDevice&) -> GpuDevice& = delete;
|
||||||
GpuDevice(GpuDevice&&) = delete;
|
GpuDevice(GpuDevice&&) = delete;
|
||||||
auto operator=(GpuDevice&&) -> GpuDevice& = delete;
|
auto operator=(GpuDevice&&) -> GpuDevice& = delete;
|
||||||
|
|
||||||
// Crea el device y claim el window. Devuelve false si no hay backend
|
// Crea el device i claim el window. Selecciona MSL en macOS, SPIR-V
|
||||||
// soportado o si el driver no permite el claim.
|
// 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;
|
[[nodiscard]] auto init(SDL_Window* window) -> bool;
|
||||||
void destroy();
|
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 get() const -> SDL_GPUDevice* { return device_; }
|
||||||
[[nodiscard]] auto window() const -> SDL_Window* { return window_; }
|
[[nodiscard]] auto window() const -> SDL_Window* { return window_; }
|
||||||
[[nodiscard]] auto swapchainFormat() const -> SDL_GPUTextureFormat { return swapchain_format_; }
|
[[nodiscard]] auto swapchainFormat() const -> SDL_GPUTextureFormat { return swapchain_format_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SDL_GPUDevice* device_{nullptr};
|
SDL_GPUDevice* device_{nullptr};
|
||||||
SDL_Window* window_{nullptr};
|
SDL_Window* window_{nullptr};
|
||||||
SDL_GPUTextureFormat swapchain_format_{SDL_GPU_TEXTUREFORMAT_INVALID};
|
SDL_GPUTextureFormat swapchain_format_{SDL_GPU_TEXTUREFORMAT_INVALID};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Rendering::GPU
|
} // 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"
|
#include "core/rendering/gpu/gpu_line_pipeline.hpp"
|
||||||
|
|
||||||
@@ -8,6 +8,15 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "core/rendering/gpu/gpu_device.hpp"
|
#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 {
|
namespace Rendering::GPU {
|
||||||
|
|
||||||
@@ -20,12 +29,37 @@ namespace Rendering::GPU {
|
|||||||
return false;
|
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,
|
SDL_GPU_SHADERSTAGE_VERTEX,
|
||||||
|
/*num_samplers=*/0,
|
||||||
/*num_uniform_buffers=*/1);
|
/*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,
|
SDL_GPU_SHADERSTAGE_FRAGMENT,
|
||||||
|
/*num_samplers=*/0,
|
||||||
/*num_uniform_buffers=*/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) || (frag == nullptr)) {
|
||||||
if (vert != nullptr) {
|
if (vert != nullptr) {
|
||||||
SDL_ReleaseGPUShader(owner_, vert);
|
SDL_ReleaseGPUShader(owner_, vert);
|
||||||
@@ -33,7 +67,7 @@ namespace Rendering::GPU {
|
|||||||
if (frag != nullptr) {
|
if (frag != nullptr) {
|
||||||
SDL_ReleaseGPUShader(owner_, frag);
|
SDL_ReleaseGPUShader(owner_, frag);
|
||||||
}
|
}
|
||||||
std::cerr << "[GpuLinePipeline] Error cargando shaders\n";
|
std::cerr << "[GpuLinePipeline] Error cargando shaders: " << SDL_GetError() << '\n';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,8 +132,8 @@ namespace Rendering::GPU {
|
|||||||
|
|
||||||
pipeline_ = SDL_CreateGPUGraphicsPipeline(owner_, &info);
|
pipeline_ = SDL_CreateGPUGraphicsPipeline(owner_, &info);
|
||||||
|
|
||||||
// Los shaders se pueden liberar tras crear el pipeline (SDL los retiene
|
// Els shaders es poden alliberar després de crear el pipeline (SDL els
|
||||||
// internamente mientras el pipeline esté vivo).
|
// reté internament mentre el pipeline estigui viu).
|
||||||
SDL_ReleaseGPUShader(owner_, vert);
|
SDL_ReleaseGPUShader(owner_, vert);
|
||||||
SDL_ReleaseGPUShader(owner_, frag);
|
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"
|
#include "core/rendering/gpu/gpu_postfx_pipeline.hpp"
|
||||||
|
|
||||||
@@ -8,91 +8,122 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "core/rendering/gpu/gpu_device.hpp"
|
#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 {
|
namespace Rendering::GPU {
|
||||||
|
|
||||||
GpuPostFxPipeline::~GpuPostFxPipeline() { destroy(); }
|
GpuPostFxPipeline::~GpuPostFxPipeline() { destroy(); }
|
||||||
|
|
||||||
auto GpuPostFxPipeline::init(const GpuDevice& device,
|
auto GpuPostFxPipeline::init(const GpuDevice& device,
|
||||||
SDL_GPUTextureFormat target_format) -> bool {
|
SDL_GPUTextureFormat target_format) -> bool {
|
||||||
owner_ = device.get();
|
owner_ = device.get();
|
||||||
if (owner_ == nullptr) {
|
if (owner_ == nullptr) {
|
||||||
return false;
|
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);
|
|
||||||
}
|
}
|
||||||
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.
|
void GpuPostFxPipeline::destroy() {
|
||||||
SDL_GPUVertexInputState vertex_input{};
|
if ((pipeline_ != nullptr) && (owner_ != nullptr)) {
|
||||||
vertex_input.vertex_buffer_descriptions = nullptr;
|
SDL_ReleaseGPUGraphicsPipeline(owner_, pipeline_);
|
||||||
vertex_input.num_vertex_buffers = 0;
|
}
|
||||||
vertex_input.vertex_attributes = nullptr;
|
pipeline_ = nullptr;
|
||||||
vertex_input.num_vertex_attributes = 0;
|
owner_ = nullptr;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GpuPostFxPipeline::destroy() {
|
|
||||||
if ((pipeline_ != nullptr) && (owner_ != nullptr)) {
|
|
||||||
SDL_ReleaseGPUGraphicsPipeline(owner_, pipeline_);
|
|
||||||
}
|
|
||||||
pipeline_ = nullptr;
|
|
||||||
owner_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Rendering::GPU
|
} // 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