Compare commits
2 Commits
main
...
metal-inte
| Author | SHA1 | Date | |
|---|---|---|---|
| b3f6e2fcf0 | |||
| 7f00942517 |
@@ -219,7 +219,7 @@ constexpr SDL_ScaleMode VIDEO_SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
|
|||||||
constexpr bool VIDEO_FULLSCREEN = false;
|
constexpr bool VIDEO_FULLSCREEN = false;
|
||||||
constexpr bool VIDEO_VSYNC = true;
|
constexpr bool VIDEO_VSYNC = true;
|
||||||
constexpr bool VIDEO_INTEGER_SCALE = true;
|
constexpr bool VIDEO_INTEGER_SCALE = true;
|
||||||
constexpr bool VIDEO_SHADERS = false;
|
constexpr bool VIDEO_SHADERS = true;
|
||||||
|
|
||||||
// Music
|
// Music
|
||||||
constexpr bool MUSIC_ENABLED = true;
|
constexpr bool MUSIC_ENABLED = true;
|
||||||
|
|||||||
16
source/external/jail_shader.h
vendored
16
source/external/jail_shader.h
vendored
@@ -4,6 +4,20 @@
|
|||||||
#include <string> // Para basic_string, string
|
#include <string> // Para basic_string, string
|
||||||
|
|
||||||
namespace shader {
|
namespace shader {
|
||||||
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = "");
|
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &shaderSource, const std::string &fragmentShader = "");
|
||||||
void render();
|
void render();
|
||||||
|
void cleanup();
|
||||||
|
bool isUsingOpenGL();
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
namespace metal {
|
||||||
|
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename);
|
||||||
|
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height);
|
||||||
|
void updateMetalTexture(SDL_Texture* backBuffer);
|
||||||
|
void renderMetal();
|
||||||
|
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture);
|
||||||
|
void cleanupMetal();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace shader
|
} // namespace shader
|
||||||
387
source/external/jail_shader_metal.mm
vendored
Normal file
387
source/external/jail_shader_metal.mm
vendored
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
#include "jail_shader.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_metal.h>
|
||||||
|
#include <Metal/Metal.h>
|
||||||
|
#include <QuartzCore/CAMetalLayer.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include "../asset.h"
|
||||||
|
|
||||||
|
namespace shader {
|
||||||
|
namespace metal {
|
||||||
|
|
||||||
|
// Metal objects
|
||||||
|
id<MTLDevice> device = nullptr;
|
||||||
|
id<MTLRenderPipelineState> pipelineState = nullptr;
|
||||||
|
id<MTLBuffer> vertexBuffer = nullptr;
|
||||||
|
id<MTLTexture> backBufferTexture = nullptr;
|
||||||
|
id<MTLTexture> gameCanvasTexture = nullptr; // Our custom render target texture
|
||||||
|
id<MTLSamplerState> sampler = nullptr;
|
||||||
|
|
||||||
|
// SDL objects (references from main shader module)
|
||||||
|
SDL_Window* win = nullptr;
|
||||||
|
SDL_Renderer* renderer = nullptr;
|
||||||
|
SDL_Texture* backBuffer = nullptr;
|
||||||
|
|
||||||
|
// Vertex data for fullscreen quad
|
||||||
|
struct Vertex {
|
||||||
|
float position[4]; // x, y, z, w
|
||||||
|
float texcoord[2]; // u, v
|
||||||
|
};
|
||||||
|
|
||||||
|
const Vertex quadVertices[] = {
|
||||||
|
// Position (x, y, z, w) // TexCoord (u, v) - Standard OpenGL-style coordinates
|
||||||
|
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, // Bottom-left
|
||||||
|
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, // Bottom-right
|
||||||
|
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, // Top-left
|
||||||
|
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, // Top-right
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string loadMetalShader(const std::string& filename) {
|
||||||
|
// Try to load the .metal file from the same location as GLSL files
|
||||||
|
auto data = Asset::get()->loadData(filename);
|
||||||
|
if (!data.empty()) {
|
||||||
|
return std::string(data.begin(), data.end());
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height) {
|
||||||
|
if (!renderer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "createMetalRenderTarget: No renderer provided");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear textura Metal como render target
|
||||||
|
SDL_Texture* metalTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
|
||||||
|
if (!metalTexture) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render target texture: %s", SDL_GetError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configurar filtrado nearest neighbor para look pixelado
|
||||||
|
SDL_SetTextureScaleMode(metalTexture, SDL_SCALEMODE_NEAREST);
|
||||||
|
|
||||||
|
// Try to extract and store the Metal texture directly
|
||||||
|
SDL_PropertiesID props = SDL_GetTextureProperties(metalTexture);
|
||||||
|
if (props != 0) {
|
||||||
|
const char* propertyNames[] = {
|
||||||
|
"SDL.texture.metal.texture",
|
||||||
|
"SDL.renderer.metal.texture",
|
||||||
|
"metal.texture",
|
||||||
|
"texture.metal",
|
||||||
|
"MTLTexture",
|
||||||
|
"texture"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* propName : propertyNames) {
|
||||||
|
gameCanvasTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
||||||
|
if (gameCanvasTexture) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Successfully extracted Metal texture via property '%s' (size: %lux%lu)",
|
||||||
|
propName, [gameCanvasTexture width], [gameCanvasTexture height]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameCanvasTexture) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not extract Metal texture from SDL texture - shaders may not work");
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Created Metal render target texture (%dx%d)", width, height);
|
||||||
|
return metalTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool initMetal(SDL_Window* window, SDL_Texture* backBufferTexture, const std::string& shaderFilename) {
|
||||||
|
// Store references
|
||||||
|
win = window;
|
||||||
|
backBuffer = backBufferTexture;
|
||||||
|
renderer = SDL_GetRenderer(window);
|
||||||
|
|
||||||
|
if (!renderer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL renderer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Metal layer from SDL renderer and extract device from it
|
||||||
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*)SDL_GetRenderMetalLayer(renderer);
|
||||||
|
if (!metalLayer) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal layer from SDL renderer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = metalLayer.device;
|
||||||
|
if (!device) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal device from layer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal device from SDL layer: %s", [[device name] UTF8String]);
|
||||||
|
|
||||||
|
// Note: We no longer need our own texture - we'll use the backBuffer directly
|
||||||
|
|
||||||
|
// Load and compile shaders
|
||||||
|
std::string metalShaderSource = loadMetalShader(shaderFilename);
|
||||||
|
if (metalShaderSource.empty()) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader: %s", shaderFilename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded Metal shader %s (length: %zu)",
|
||||||
|
shaderFilename.c_str(), metalShaderSource.length());
|
||||||
|
|
||||||
|
NSString* shaderNSString = [NSString stringWithUTF8String:metalShaderSource.c_str()];
|
||||||
|
NSError* error = nil;
|
||||||
|
id<MTLLibrary> library = [device newLibraryWithSource:shaderNSString options:nil error:&error];
|
||||||
|
if (!library || error) {
|
||||||
|
if (error) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile Metal shader: %s",
|
||||||
|
[[error localizedDescription] UTF8String]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
|
||||||
|
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];
|
||||||
|
|
||||||
|
if (!vertexFunction || !fragmentFunction) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader functions");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create render pipeline
|
||||||
|
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
|
||||||
|
pipelineDescriptor.vertexFunction = vertexFunction;
|
||||||
|
pipelineDescriptor.fragmentFunction = fragmentFunction;
|
||||||
|
pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
||||||
|
|
||||||
|
// Set up vertex descriptor
|
||||||
|
MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
|
||||||
|
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat4;
|
||||||
|
vertexDescriptor.attributes[0].offset = 0;
|
||||||
|
vertexDescriptor.attributes[0].bufferIndex = 0;
|
||||||
|
vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2;
|
||||||
|
vertexDescriptor.attributes[1].offset = 4 * sizeof(float);
|
||||||
|
vertexDescriptor.attributes[1].bufferIndex = 0;
|
||||||
|
vertexDescriptor.layouts[0].stride = sizeof(Vertex);
|
||||||
|
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
|
||||||
|
|
||||||
|
pipelineDescriptor.vertexDescriptor = vertexDescriptor;
|
||||||
|
|
||||||
|
pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
|
||||||
|
if (!pipelineState || error) {
|
||||||
|
if (error) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render pipeline: %s",
|
||||||
|
[[error localizedDescription] UTF8String]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create vertex buffer
|
||||||
|
vertexBuffer = [device newBufferWithBytes:quadVertices
|
||||||
|
length:sizeof(quadVertices)
|
||||||
|
options:MTLResourceOptionCPUCacheModeDefault];
|
||||||
|
|
||||||
|
// Create sampler state for nearest neighbor filtering (pixelated look)
|
||||||
|
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
|
||||||
|
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
|
||||||
|
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
|
||||||
|
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||||
|
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
|
||||||
|
|
||||||
|
sampler = [device newSamplerStateWithDescriptor:samplerDescriptor];
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Metal shader system initialized successfully");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateMetalTexture(SDL_Texture* backBuffer) {
|
||||||
|
if (!device || !backBuffer) return;
|
||||||
|
|
||||||
|
// Only log this occasionally to avoid spam
|
||||||
|
static int attemptCount = 0;
|
||||||
|
static bool hasLogged = false;
|
||||||
|
|
||||||
|
// Try multiple property names that SDL3 might use
|
||||||
|
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
|
||||||
|
if (props != 0) {
|
||||||
|
const char* propertyNames[] = {
|
||||||
|
"SDL.texture.metal.texture",
|
||||||
|
"SDL.renderer.metal.texture",
|
||||||
|
"metal.texture",
|
||||||
|
"texture.metal",
|
||||||
|
"MTLTexture",
|
||||||
|
"texture"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const char* propName : propertyNames) {
|
||||||
|
id<MTLTexture> sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
|
||||||
|
if (sdlMetalTexture) {
|
||||||
|
backBufferTexture = sdlMetalTexture;
|
||||||
|
if (!hasLogged) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal texture via property '%s' (size: %lux%lu)",
|
||||||
|
propName, [backBufferTexture width], [backBufferTexture height]);
|
||||||
|
hasLogged = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can't get the texture after several attempts, log once and continue
|
||||||
|
if (!hasLogged && attemptCount++ > 10) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not access SDL Metal texture after %d attempts - shader will be skipped", attemptCount);
|
||||||
|
hasLogged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderMetal() {
|
||||||
|
if (!renderer || !device || !pipelineState) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal render failed: missing components");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correct pipeline: backBuffer → Metal shaders → screen (no double rendering)
|
||||||
|
|
||||||
|
// Try to get the Metal texture directly from the SDL backBuffer texture
|
||||||
|
updateMetalTexture(backBuffer);
|
||||||
|
|
||||||
|
if (!backBufferTexture) {
|
||||||
|
static int fallbackLogCount = 0;
|
||||||
|
if (fallbackLogCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Metal texture not available - falling back to normal SDL rendering (attempt %d)", fallbackLogCount);
|
||||||
|
}
|
||||||
|
// Fallback: render without shaders using normal SDL path
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Metal CRT shader directly: backBuffer texture → screen
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Get Metal command encoder to render directly to screen
|
||||||
|
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
|
||||||
|
if (encoder_ptr) {
|
||||||
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
|
||||||
|
|
||||||
|
static int debugCount = 0;
|
||||||
|
if (debugCount++ < 5) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal render attempt %d: encoder=%p, pipeline=%p, texture=%p",
|
||||||
|
debugCount, encoder, pipelineState, backBufferTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply CRT shader effect directly to backBuffer texture
|
||||||
|
[encoder setRenderPipelineState:pipelineState];
|
||||||
|
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
||||||
|
[encoder setFragmentTexture:backBufferTexture atIndex:0];
|
||||||
|
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||||
|
|
||||||
|
// Draw fullscreen quad with CRT effect directly to screen
|
||||||
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
|
static int successCount = 0;
|
||||||
|
if (successCount++ < 5) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT shader to backBuffer (attempt %d) - texture size: %lux%lu",
|
||||||
|
successCount, [backBufferTexture width], [backBufferTexture height]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
|
||||||
|
|
||||||
|
static int fallbackCount = 0;
|
||||||
|
if (fallbackCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder - fallback rendering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture) {
|
||||||
|
if (!renderer || !sourceTexture || !device || !pipelineState) {
|
||||||
|
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing failed: missing components");
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use our stored Metal texture if available
|
||||||
|
id<MTLTexture> metalTexture = gameCanvasTexture;
|
||||||
|
|
||||||
|
if (!metalTexture) {
|
||||||
|
static int fallbackLogCount = 0;
|
||||||
|
if (fallbackLogCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "gameCanvasTexture not available - falling back to normal rendering (attempt %d)", fallbackLogCount);
|
||||||
|
}
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply Metal CRT shader post-processing: sourceTexture → screen
|
||||||
|
SDL_SetRenderTarget(renderer, nullptr);
|
||||||
|
|
||||||
|
// Get Metal command encoder to render directly to screen
|
||||||
|
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
|
||||||
|
if (encoder_ptr) {
|
||||||
|
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
|
||||||
|
|
||||||
|
static int debugCount = 0;
|
||||||
|
if (debugCount++ < 3) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing attempt %d: encoder=%p, pipeline=%p, texture=%p",
|
||||||
|
debugCount, encoder, pipelineState, metalTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply CRT shader effect to sourceTexture
|
||||||
|
[encoder setRenderPipelineState:pipelineState];
|
||||||
|
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
|
||||||
|
[encoder setFragmentTexture:metalTexture atIndex:0];
|
||||||
|
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||||
|
|
||||||
|
// Draw fullscreen quad with CRT effect directly to screen
|
||||||
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
|
|
||||||
|
static int successCount = 0;
|
||||||
|
if (successCount++ < 3) {
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT post-processing (attempt %d) - texture size: %lux%lu",
|
||||||
|
successCount, [metalTexture width], [metalTexture height]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback: render normally without shaders
|
||||||
|
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
|
||||||
|
|
||||||
|
static int fallbackCount = 0;
|
||||||
|
if (fallbackCount++ < 3) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder for post-processing - fallback rendering");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderPresent(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanupMetal() {
|
||||||
|
// Release Metal objects (ARC handles most of this automatically)
|
||||||
|
pipelineState = nullptr;
|
||||||
|
backBufferTexture = nullptr;
|
||||||
|
gameCanvasTexture = nullptr;
|
||||||
|
vertexBuffer = nullptr;
|
||||||
|
sampler = nullptr;
|
||||||
|
device = nullptr;
|
||||||
|
renderer = nullptr;
|
||||||
|
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal shader system cleaned up");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace metal
|
||||||
|
} // namespace shader
|
||||||
|
|
||||||
|
#endif // __APPLE__
|
||||||
@@ -44,8 +44,21 @@ Screen::Screen()
|
|||||||
initSDLVideo();
|
initSDLVideo();
|
||||||
|
|
||||||
// Crea la textura de destino
|
// Crea la textura de destino
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const auto render_name = SDL_GetRendererName(renderer_);
|
||||||
|
if (render_name && !strncmp(render_name, "metal", 5)) {
|
||||||
|
// Usar nuestra propia Metal texture como render target
|
||||||
|
game_canvas_ = shader::metal::createMetalRenderTarget(renderer_, param.game.width, param.game.height);
|
||||||
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using custom Metal render target for game_canvas_");
|
||||||
|
} else {
|
||||||
|
// Fallback para otros renderers
|
||||||
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
||||||
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
|
||||||
|
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Crea el objeto de texto
|
// Crea el objeto de texto
|
||||||
createText();
|
createText();
|
||||||
@@ -99,7 +112,18 @@ void Screen::renderPresent() {
|
|||||||
clean();
|
clean();
|
||||||
|
|
||||||
if (Options::video.shaders) {
|
if (Options::video.shaders) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
const auto render_name = SDL_GetRendererName(renderer_);
|
||||||
|
if (render_name && !strncmp(render_name, "metal", 5)) {
|
||||||
|
// Use Metal post-processing with our custom render target
|
||||||
|
shader::metal::renderWithPostProcessing(renderer_, game_canvas_);
|
||||||
|
} else {
|
||||||
|
// Fallback to standard shader system for non-Metal renderers
|
||||||
shader::render();
|
shader::render();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
shader::render();
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
|
||||||
SDL_RenderPresent(renderer_);
|
SDL_RenderPresent(renderer_);
|
||||||
@@ -296,17 +320,29 @@ auto Screen::initSDLVideo() -> bool {
|
|||||||
// Obtener información de la pantalla
|
// Obtener información de la pantalla
|
||||||
getDisplayInfo();
|
getDisplayInfo();
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Configurar hint para Metal
|
||||||
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
|
||||||
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
|
"Warning: Failed to set Metal hint!");
|
||||||
|
}
|
||||||
|
// Configurar flags para la creación de la ventana
|
||||||
|
SDL_WindowFlags window_flags = SDL_WINDOW_METAL;
|
||||||
|
#else // NOT APPLE
|
||||||
// Configurar hint para OpenGL
|
// Configurar hint para OpenGL
|
||||||
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
|
||||||
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
|
||||||
"Warning: Failed to set OpenGL hint!");
|
"Warning: Failed to set OpenGL hint!");
|
||||||
}
|
}
|
||||||
|
// Configurar flags para la creación de la ventana
|
||||||
// Crear ventana
|
|
||||||
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
|
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
|
||||||
|
#endif
|
||||||
|
// Configurar flags para la creación de la ventana
|
||||||
if (Options::video.fullscreen) {
|
if (Options::video.fullscreen) {
|
||||||
window_flags |= SDL_WINDOW_FULLSCREEN;
|
window_flags |= SDL_WINDOW_FULLSCREEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Crear ventana
|
||||||
window_ = SDL_CreateWindow(
|
window_ = SDL_CreateWindow(
|
||||||
Options::window.caption.c_str(),
|
Options::window.caption.c_str(),
|
||||||
param.game.width * Options::window.zoom,
|
param.game.width * Options::window.zoom,
|
||||||
|
|||||||
Reference in New Issue
Block a user