| | |
| | | #if SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED |
| | | |
| | | #include "SDL_hints.h" |
| | | #include "SDL_log.h" |
| | | #include "SDL_assert.h" |
| | | #include "SDL_syswm.h" |
| | | #include "SDL_metal.h" |
| | | #include "../SDL_sysrender.h" |
| | |
| | | #ifdef __MACOSX__ |
| | | #include "SDL_shaders_metal_osx.h" |
| | | #elif defined(__TVOS__) |
| | | #if TARGET_OS_SIMULATOR |
| | | #include "SDL_shaders_metal_tvsimulator.h" |
| | | #else |
| | | #include "SDL_shaders_metal_tvos.h" |
| | | #endif |
| | | #else |
| | | #if TARGET_OS_SIMULATOR |
| | | #include "SDL_shaders_metal_iphonesimulator.h" |
| | | #else |
| | | #include "SDL_shaders_metal_ios.h" |
| | | #endif |
| | | #endif |
| | | |
| | | /* Apple Metal renderer implementation */ |
| | | |
| | | /* Used to re-create the window with Metal capability */ |
| | | extern int SDL_RecreateWindow(SDL_Window * window, Uint32 flags); |
| | | |
| | | /* macOS requires constants in a buffer to have a 256 byte alignment. */ |
| | | /* Use native type alignments from https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf */ |
| | | #ifdef __MACOSX__ |
| | | #if defined(__MACOSX__) || TARGET_OS_SIMULATOR |
| | | #define CONSTANT_ALIGN(x) (256) |
| | | #else |
| | | #define CONSTANT_ALIGN(x) (x < 4 ? 4 : x) |
| | |
| | | } |
| | | |
| | | static int |
| | | METAL_QueueDrawLines(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) |
| | | { |
| | | SDL_assert(count >= 2); /* should have been checked at the higher level. */ |
| | | |
| | | const size_t vertlen = (sizeof (float) * 2) * count; |
| | | float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, DEVICE_ALIGN(8), &cmd->data.draw.first); |
| | | if (!verts) { |
| | | return -1; |
| | | } |
| | | cmd->data.draw.count = count; |
| | | SDL_memcpy(verts, points, vertlen); |
| | | |
| | | /* If the line segment is completely horizontal or vertical, |
| | | make it one pixel longer, to satisfy the diamond-exit rule. |
| | | We should probably do this for diagonal lines too, but we'd have to |
| | | do some trigonometry to figure out the correct pixel and generally |
| | | when we have problems with pixel perfection, it's for straight lines |
| | | that are missing a pixel that frames something and not arbitrary |
| | | angles. Maybe !!! FIXME for later, though. */ |
| | | |
| | | points += count - 2; /* update the last line. */ |
| | | verts += (count * 2) - 2; |
| | | |
| | | const float xstart = points[0].x; |
| | | const float ystart = points[0].y; |
| | | const float xend = points[1].x; |
| | | const float yend = points[1].y; |
| | | |
| | | if (ystart == yend) { /* horizontal line */ |
| | | verts[0] += (xend > xstart) ? 1.0f : -1.0f; |
| | | } else if (xstart == xend) { /* vertical line */ |
| | | verts[1] += (yend > ystart) ? 1.0f : -1.0f; |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | static int |
| | | METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) |
| | | { |
| | | const size_t vertlen = (sizeof (float) * 8) * count; |
| | |
| | | { @autoreleasepool { |
| | | METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata; |
| | | |
| | | if (data.mtlcmdencoder != nil) { |
| | | [data.mtlcmdencoder endEncoding]; |
| | | // If we don't have a command buffer, we can't present, so activate to get one. |
| | | if (data.mtlcmdencoder == nil) { |
| | | // We haven't even gotten a backbuffer yet? Clear it to black. Otherwise, load the existing data. |
| | | if (data.mtlbackbuffer == nil) { |
| | | MTLClearColor color = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f); |
| | | METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear, &color, nil); |
| | | } else { |
| | | METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL, nil); |
| | | } |
| | | } |
| | | if (data.mtlbackbuffer != nil) { |
| | | [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer]; |
| | | } |
| | | if (data.mtlcmdbuffer != nil) { |
| | | [data.mtlcmdbuffer commit]; |
| | | } |
| | | |
| | | [data.mtlcmdencoder endEncoding]; |
| | | [data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer]; |
| | | [data.mtlcmdbuffer commit]; |
| | | |
| | | data.mtlcmdencoder = nil; |
| | | data.mtlcmdbuffer = nil; |
| | | data.mtlbackbuffer = nil; |
| | |
| | | SDL_MetalView view = NULL; |
| | | CAMetalLayer *layer = nil; |
| | | SDL_SysWMinfo syswm; |
| | | Uint32 window_flags; |
| | | SDL_bool changed_window = SDL_FALSE; |
| | | |
| | | SDL_VERSION(&syswm.version); |
| | | if (!SDL_GetWindowWMInfo(window, &syswm)) { |
| | |
| | | return NULL; |
| | | } |
| | | |
| | | window_flags = SDL_GetWindowFlags(window); |
| | | if (!(window_flags & SDL_WINDOW_METAL)) { |
| | | changed_window = SDL_TRUE; |
| | | if (SDL_RecreateWindow(window, (window_flags & ~(SDL_WINDOW_VULKAN | SDL_WINDOW_OPENGL)) | SDL_WINDOW_METAL) < 0) { |
| | | return NULL; |
| | | } |
| | | } |
| | | |
| | | renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); |
| | | if (!renderer) { |
| | | SDL_OutOfMemory(); |
| | | if (changed_window) { |
| | | SDL_RecreateWindow(window, window_flags); |
| | | } |
| | | return NULL; |
| | | } |
| | | |
| | |
| | | if (mtldevice == nil) { |
| | | SDL_free(renderer); |
| | | SDL_SetError("Failed to obtain Metal device"); |
| | | if (changed_window) { |
| | | SDL_RecreateWindow(window, window_flags); |
| | | } |
| | | return NULL; |
| | | } |
| | | |
| | |
| | | [mtldevice release]; |
| | | #endif |
| | | SDL_free(renderer); |
| | | if (changed_window) { |
| | | SDL_RecreateWindow(window, window_flags); |
| | | } |
| | | return NULL; |
| | | } |
| | | |
| | |
| | | #endif |
| | | SDL_Metal_DestroyView(view); |
| | | SDL_free(renderer); |
| | | if (changed_window) { |
| | | SDL_RecreateWindow(window, window_flags); |
| | | } |
| | | return NULL; |
| | | } |
| | | |
| | |
| | | renderer->QueueSetViewport = METAL_QueueSetViewport; |
| | | renderer->QueueSetDrawColor = METAL_QueueSetDrawColor; |
| | | renderer->QueueDrawPoints = METAL_QueueDrawPoints; |
| | | renderer->QueueDrawLines = METAL_QueueDrawPoints; // lines and points queue the same way. |
| | | renderer->QueueDrawLines = METAL_QueueDrawLines; |
| | | renderer->QueueFillRects = METAL_QueueFillRects; |
| | | renderer->QueueCopy = METAL_QueueCopy; |
| | | renderer->QueueCopyEx = METAL_QueueCopyEx; |