Skip to content

Latest commit

 

History

History
781 lines (664 loc) · 31.3 KB

SDL_render_metal.m

File metadata and controls

781 lines (664 loc) · 31.3 KB
 
1
2
/*
Simple DirectMedia Layer
Dec 8, 2017
Dec 8, 2017
3
Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#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_sysrender.h"
Dec 8, 2017
Dec 8, 2017
31
#ifdef __MACOSX__
32
#include <Cocoa/Cocoa.h>
Dec 8, 2017
Dec 8, 2017
33
34
35
#else
#include "../../video/uikit/SDL_uikitmetalview.h"
#endif
36
37
38
#include <Metal/Metal.h>
#include <QuartzCore/CAMetalLayer.h>
Dec 8, 2017
Dec 8, 2017
39
40
41
42
43
44
/* Regenerate these with build-metal-shaders.sh */
#ifdef __MACOSX__
#include "SDL_shaders_metal_osx.h"
#else
#include "SDL_shaders_metal_ios.h"
#endif
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/* Apple Metal renderer implementation */
static SDL_Renderer *METAL_CreateRenderer(SDL_Window * window, Uint32 flags);
static void METAL_WindowEvent(SDL_Renderer * renderer,
const SDL_WindowEvent *event);
static int METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h);
static int METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static int METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels,
int pitch);
static int METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect,
const Uint8 *Yplane, int Ypitch,
const Uint8 *Uplane, int Upitch,
const Uint8 *Vplane, int Vpitch);
static int METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, void **pixels, int *pitch);
static void METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static int METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
static int METAL_UpdateViewport(SDL_Renderer * renderer);
static int METAL_UpdateClipRect(SDL_Renderer * renderer);
static int METAL_RenderClear(SDL_Renderer * renderer);
static int METAL_RenderDrawPoints(SDL_Renderer * renderer,
const SDL_FPoint * points, int count);
static int METAL_RenderDrawLines(SDL_Renderer * renderer,
const SDL_FPoint * points, int count);
static int METAL_RenderFillRects(SDL_Renderer * renderer,
const SDL_FRect * rects, int count);
static int METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect);
static int METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect,
const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
static int METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 pixel_format, void * pixels, int pitch);
static void METAL_RenderPresent(SDL_Renderer * renderer);
static void METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture);
static void METAL_DestroyRenderer(SDL_Renderer * renderer);
SDL_RenderDriver METAL_RenderDriver = {
METAL_CreateRenderer,
{
"metal",
(SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
2,
{SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
4096, // !!! FIXME: how do you query Metal for this?
4096}
};
Dec 8, 2017
Dec 8, 2017
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
@interface METAL_RenderData : NSObject
@property (atomic, retain) id<MTLDevice> mtldevice;
@property (atomic, retain) id<MTLCommandQueue> mtlcmdqueue;
@property (atomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
@property (atomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
@property (atomic, retain) id<MTLLibrary> mtllibrary;
@property (atomic, retain) id<CAMetalDrawable> mtlbackbuffer;
@property (atomic, retain) NSMutableArray *mtlpipelineprims;
@property (atomic, retain) NSMutableArray *mtlpipelinecopy;
@property (atomic, retain) id<MTLBuffer> mtlbufclearverts;
@property (atomic, retain) CAMetalLayer *mtllayer;
@property (atomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
@end
@implementation METAL_RenderData
@end
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
static int
IsMetalAvailable(const SDL_SysWMinfo *syswm)
{
if (syswm->subsystem != SDL_SYSWM_COCOA) { // !!! FIXME: SDL_SYSWM_UIKIT for iOS, too!
return SDL_SetError("Metal render target only supports Cocoa video target at the moment.");
}
// this checks a weak symbol.
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
if (MTLCreateSystemDefaultDevice == NULL) { // probably on 10.10 or lower.
return SDL_SetError("Metal framework not available on this system");
}
#endif
return 0;
}
static id<MTLRenderPipelineState>
MakePipelineState(METAL_RenderData *data, NSString *label, NSString *vertfn,
NSString *fragfn, const SDL_BlendMode blendmode)
{
Dec 8, 2017
Dec 8, 2017
134
135
id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:vertfn];
id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:fragfn];
136
137
138
139
140
141
SDL_assert(mtlvertfn != nil);
SDL_assert(mtlfragfn != nil);
MTLRenderPipelineDescriptor *mtlpipedesc = [[MTLRenderPipelineDescriptor alloc] init];
mtlpipedesc.vertexFunction = mtlvertfn;
mtlpipedesc.fragmentFunction = mtlfragfn;
Dec 8, 2017
Dec 8, 2017
142
mtlpipedesc.colorAttachments[0].pixelFormat = data.mtlbackbuffer.texture.pixelFormat;
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
switch (blendmode) {
case SDL_BLENDMODE_BLEND:
mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
break;
case SDL_BLENDMODE_ADD:
mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOne;
mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
break;
case SDL_BLENDMODE_MOD:
mtlpipedesc.colorAttachments[0].blendingEnabled = YES;
mtlpipedesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
mtlpipedesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
mtlpipedesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorZero;
mtlpipedesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorSourceColor;
mtlpipedesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorZero;
mtlpipedesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOne;
break;
Dec 8, 2017
Dec 8, 2017
174
175
176
177
default:
mtlpipedesc.colorAttachments[0].blendingEnabled = NO;
break;
178
179
180
181
182
}
mtlpipedesc.label = label;
NSError *err = nil;
Dec 8, 2017
Dec 8, 2017
183
id<MTLRenderPipelineState> retval = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
184
SDL_assert(err == nil);
Dec 8, 2017
Dec 8, 2017
185
#if !__has_feature(objc_arc)
186
187
188
189
[mtlpipedesc release]; // !!! FIXME: can these be reused for each creation, or does the pipeline obtain it?
[mtlvertfn release];
[mtlfragfn release];
[label release];
Dec 8, 2017
Dec 8, 2017
190
#endif
191
192
193
194
return retval;
}
static void
Dec 8, 2017
Dec 8, 2017
195
MakePipelineStates(METAL_RenderData *data, NSMutableArray *states,
196
197
NSString *label, NSString *vertfn, NSString *fragfn)
{
Dec 8, 2017
Dec 8, 2017
198
199
200
201
[states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=none)"], vertfn, fragfn, SDL_BLENDMODE_NONE)];
[states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=blend)"], vertfn, fragfn, SDL_BLENDMODE_BLEND)];
[states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=add)"], vertfn, fragfn, SDL_BLENDMODE_ADD)];
[states addObject:MakePipelineState(data, [label stringByAppendingString:@" (blendmode=mod)"], vertfn, fragfn, SDL_BLENDMODE_MOD)];
202
203
204
}
static inline id<MTLRenderPipelineState>
Dec 8, 2017
Dec 8, 2017
205
ChoosePipelineState(NSMutableArray *states, const SDL_BlendMode blendmode)
206
207
{
switch (blendmode) {
Dec 8, 2017
Dec 8, 2017
208
209
210
211
case SDL_BLENDMODE_BLEND: return (id<MTLRenderPipelineState>)states[1];
case SDL_BLENDMODE_ADD: return (id<MTLRenderPipelineState>)states[2];
case SDL_BLENDMODE_MOD: return (id<MTLRenderPipelineState>)states[3];
default: return (id<MTLRenderPipelineState>)states[0];
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
}
return nil;
}
static SDL_Renderer *
METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
{
SDL_Renderer *renderer = NULL;
METAL_RenderData *data = NULL;
SDL_SysWMinfo syswm;
SDL_VERSION(&syswm.version);
if (!SDL_GetWindowWMInfo(window, &syswm)) {
return NULL;
}
if (IsMetalAvailable(&syswm) == -1) {
return NULL;
}
renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
if (!renderer) {
SDL_OutOfMemory();
return NULL;
}
Dec 8, 2017
Dec 8, 2017
238
239
240
241
242
data = [[METAL_RenderData alloc] init];
#if __has_feature(objc_arc)
renderer->driverdata = (void*)CFBridgingRetain(data);
#else
243
renderer->driverdata = data;
Dec 8, 2017
Dec 8, 2017
244
#endif
245
246
renderer->window = window;
Dec 8, 2017
Dec 8, 2017
247
248
249
#ifdef __MACOSX__
id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice(); // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
if (mtldevice == nil) {
250
SDL_free(renderer);
Dec 8, 2017
Dec 8, 2017
251
252
253
#if !__has_feature(objc_arc)
[data release];
#endif
254
255
256
257
258
259
260
261
SDL_SetError("Failed to obtain Metal device");
return NULL;
}
// !!! FIXME: error checking on all of this.
NSView *nsview = [syswm.info.cocoa.window contentView];
Dec 8, 2017
Dec 8, 2017
262
263
// CAMetalLayer is available in QuartzCore starting at OSX 10.11
CAMetalLayer *layer = [NSClassFromString( @"CAMetalLayer" ) layer];
Dec 8, 2017
Dec 8, 2017
265
layer.device = mtldevice;
266
267
268
269
270
271
272
273
274
//layer.pixelFormat = MTLPixelFormatBGRA8Unorm; // !!! FIXME: MTLPixelFormatBGRA8Unorm_sRGB ?
layer.framebufferOnly = YES;
//layer.drawableSize = (CGSize) [nsview convertRectToBacking:[nsview bounds]].size;
//layer.colorspace = nil;
[nsview setWantsLayer:YES];
[nsview setLayer:layer];
[layer retain];
Dec 8, 2017
Dec 8, 2017
275
#else
Dec 8, 2017
Dec 8, 2017
276
277
UIView *view = UIKit_Mtl_AddMetalView(window);
CAMetalLayer *layer = (CAMetalLayer *)[view layer];
Dec 8, 2017
Dec 8, 2017
278
279
#endif
Dec 8, 2017
Dec 8, 2017
280
281
282
283
data.mtldevice = layer.device;
data.mtllayer = layer;
data.mtlcmdqueue = [data.mtldevice newCommandQueue];
data.mtlcmdqueue.label = @"SDL Metal Renderer";
Dec 8, 2017
Dec 8, 2017
285
data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor]; // !!! FIXME: is this autoreleased?
286
287
// we don't specify a depth or stencil buffer because the render API doesn't currently use them.
Dec 8, 2017
Dec 8, 2017
288
289
290
MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
data.mtlbackbuffer = [data.mtllayer nextDrawable];
colorAttachment.texture = data.mtlbackbuffer.texture;
291
292
colorAttachment.loadAction = MTLLoadActionClear;
colorAttachment.clearColor = MTLClearColorMake(0.0f, 0.0f, 0.0f, 1.0f);
Dec 8, 2017
Dec 8, 2017
293
data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
294
295
// Just push a clear to the screen to start so we're in a good state.
Dec 8, 2017
Dec 8, 2017
296
297
data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
data.mtlcmdencoder.label = @"Initial drawable clear";
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
METAL_RenderPresent(renderer);
renderer->WindowEvent = METAL_WindowEvent;
renderer->GetOutputSize = METAL_GetOutputSize;
renderer->CreateTexture = METAL_CreateTexture;
renderer->UpdateTexture = METAL_UpdateTexture;
renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
renderer->LockTexture = METAL_LockTexture;
renderer->UnlockTexture = METAL_UnlockTexture;
renderer->SetRenderTarget = METAL_SetRenderTarget;
renderer->UpdateViewport = METAL_UpdateViewport;
renderer->UpdateClipRect = METAL_UpdateClipRect;
renderer->RenderClear = METAL_RenderClear;
renderer->RenderDrawPoints = METAL_RenderDrawPoints;
renderer->RenderDrawLines = METAL_RenderDrawLines;
renderer->RenderFillRects = METAL_RenderFillRects;
renderer->RenderCopy = METAL_RenderCopy;
renderer->RenderCopyEx = METAL_RenderCopyEx;
renderer->RenderReadPixels = METAL_RenderReadPixels;
renderer->RenderPresent = METAL_RenderPresent;
renderer->DestroyTexture = METAL_DestroyTexture;
renderer->DestroyRenderer = METAL_DestroyRenderer;
renderer->info = METAL_RenderDriver.info;
renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
// !!! FIXME: how do you control this in Metal?
renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
NSError *err = nil;
Dec 8, 2017
Dec 8, 2017
330
331
// The compiled .metallib is embedded in a static array in a header file
// but the original shader source code is in SDL_shaders_metal.metal.
332
dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
Dec 8, 2017
Dec 8, 2017
333
data.mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
334
SDL_assert(err == nil);
Dec 8, 2017
Dec 8, 2017
335
#if !__has_feature(objc_arc)
336
dispatch_release(mtllibdata);
Dec 8, 2017
Dec 8, 2017
337
338
#endif
data.mtllibrary.label = @"SDL Metal renderer shader library";
Dec 8, 2017
Dec 8, 2017
340
341
342
343
data.mtlpipelineprims = [[NSMutableArray alloc] init];
MakePipelineStates(data, data.mtlpipelineprims, @"SDL primitives pipeline", @"SDL_Simple_vertex", @"SDL_Simple_fragment");
data.mtlpipelinecopy = [[NSMutableArray alloc] init];
MakePipelineStates(data, data.mtlpipelinecopy, @"SDL_RenderCopy pipeline", @"SDL_Copy_vertex", @"SDL_Copy_fragment");
344
345
static const float clearverts[] = { -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 };
Dec 8, 2017
Dec 8, 2017
346
347
data.mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined|MTLResourceStorageModePrivate];
data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
// !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
return renderer;
}
static void
METAL_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
{
if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED ||
event->event == SDL_WINDOWEVENT_SHOWN ||
event->event == SDL_WINDOWEVENT_HIDDEN) {
// !!! FIXME: write me
}
}
static int
METAL_GetOutputSize(SDL_Renderer * renderer, int *w, int *h)
{
Dec 8, 2017
Dec 8, 2017
367
368
369
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
*w = (int) data.mtlbackbuffer.texture.width;
*h = (int) data.mtlbackbuffer.texture.height;
370
371
372
373
374
375
return 0;
}
static int
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
Dec 8, 2017
Dec 8, 2017
376
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
377
378
379
380
381
382
383
384
385
386
387
388
MTLPixelFormat mtlpixfmt;
switch (texture->format) {
case SDL_PIXELFORMAT_ABGR8888: mtlpixfmt = MTLPixelFormatRGBA8Unorm; break;
case SDL_PIXELFORMAT_ARGB8888: mtlpixfmt = MTLPixelFormatBGRA8Unorm; break;
default: return SDL_SetError("Texture format %s not supported by Metal", SDL_GetPixelFormatName(texture->format));
}
// !!! FIXME: autorelease or nah?
MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
Dec 8, 2017
Dec 8, 2017
389
390
id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
#if !__has_feature(objc_arc)
391
[mtltexdesc release];
Dec 8, 2017
Dec 8, 2017
392
#endif
393
394
395
396
if (mtltexture == nil) {
return SDL_SetError("Texture allocation failed");
}
Dec 8, 2017
Dec 8, 2017
397
texture->driverdata = (void*)CFBridgingRetain(mtltexture);
398
399
400
401
402
403
404
405
406
407
408
409
return 0;
}
static int
METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels, int pitch)
{
// !!! FIXME: this is a synchronous call; it doesn't return until data is uploaded in some form.
// !!! FIXME: Maybe move this off to a thread that marks the texture as uploaded and only stall the main thread if we try to
// !!! FIXME: use this texture before the marking is done? Is it worth it? Or will we basically always be uploading a bunch of
// !!! FIXME: stuff way ahead of time and/or using it immediately after upload?
Dec 8, 2017
Dec 8, 2017
410
id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
[mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
return 0;
}
static int
METAL_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect,
const Uint8 *Yplane, int Ypitch,
const Uint8 *Uplane, int Upitch,
const Uint8 *Vplane, int Vpitch)
{
return SDL_Unsupported(); // !!! FIXME
}
static int
METAL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, void **pixels, int *pitch)
{
return SDL_Unsupported(); // !!! FIXME: write me
}
static void
METAL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
// !!! FIXME: write me
}
static int
METAL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
{
Dec 8, 2017
Dec 8, 2017
441
442
443
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
id<MTLTexture> mtltexture = texture ? (__bridge id<MTLTexture>) texture->driverdata : nil;
data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
444
445
446
447
448
449
return 0;
}
static int
METAL_UpdateViewport(SDL_Renderer * renderer)
{
Dec 8, 2017
Dec 8, 2017
450
451
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
if (data.mtlcmdencoder != nil) {
452
453
454
455
456
457
458
MTLViewport viewport;
viewport.originX = renderer->viewport.x;
viewport.originY = renderer->viewport.y;
viewport.width = renderer->viewport.w;
viewport.height = renderer->viewport.h;
viewport.znear = 0.0;
viewport.zfar = 1.0;
Dec 8, 2017
Dec 8, 2017
459
[data.mtlcmdencoder setViewport:viewport];
460
461
462
463
464
465
466
467
}
return 0;
}
static int
METAL_UpdateClipRect(SDL_Renderer * renderer)
{
// !!! FIXME: should this care about the viewport?
Dec 8, 2017
Dec 8, 2017
468
469
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
if (data.mtlcmdencoder != nil) {
470
471
472
473
474
475
476
477
478
479
480
481
482
MTLScissorRect mtlrect;
if (renderer->clipping_enabled) {
const SDL_Rect *rect = &renderer->clip_rect;
mtlrect.x = renderer->viewport.x + rect->x;
mtlrect.y = renderer->viewport.x + rect->y;
mtlrect.width = rect->w;
mtlrect.height = rect->h;
} else {
mtlrect.x = renderer->viewport.x;
mtlrect.y = renderer->viewport.y;
mtlrect.width = renderer->viewport.w;
mtlrect.height = renderer->viewport.h;
}
Dec 8, 2017
Dec 8, 2017
483
[data.mtlcmdencoder setScissorRect:mtlrect];
484
485
486
487
488
489
490
491
}
return 0;
}
static int
METAL_RenderClear(SDL_Renderer * renderer)
{
// We could dump the command buffer and force a clear on a new one, but this will respect the scissor state.
Dec 8, 2017
Dec 8, 2017
492
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
493
494
495
496
497
498
// !!! FIXME: render color should live in a dedicated uniform buffer.
const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
MTLViewport viewport; // RenderClear ignores the viewport state, though, so reset that.
viewport.originX = viewport.originY = 0.0;
Dec 8, 2017
Dec 8, 2017
499
500
viewport.width = data.mtlbackbuffer.texture.width;
viewport.height = data.mtlbackbuffer.texture.height;
501
502
503
504
viewport.znear = 0.0;
viewport.zfar = 1.0;
// Draw as if we're doing a simple filled rect to the screen now.
Dec 8, 2017
Dec 8, 2017
505
506
507
508
509
[data.mtlcmdencoder setViewport:viewport];
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setVertexBuffer:data.mtlbufclearverts offset:0 atIndex:0];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
[data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
510
511
512
513
514
515
516
517
// reset the viewport for the rest of our usual drawing work...
viewport.originX = renderer->viewport.x;
viewport.originY = renderer->viewport.y;
viewport.width = renderer->viewport.w;
viewport.height = renderer->viewport.h;
viewport.znear = 0.0;
viewport.zfar = 1.0;
Dec 8, 2017
Dec 8, 2017
518
[data.mtlcmdencoder setViewport:viewport];
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
return 0;
}
// normalize a value from 0.0f to len into -1.0f to 1.0f.
static inline float
norm(const float _val, const float len)
{
const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
return ((val / len) * 2.0f) - 1.0f; // !!! FIXME: is this right?
}
// normalize a value from 0.0f to len into -1.0f to 1.0f.
static inline float
normy(const float _val, const float len)
{
return norm(len - ((_val < 0.0f) ? 0.0f : (_val > len) ? len : _val), len);
}
// normalize a value from 0.0f to len into 0.0f to 1.0f.
static inline float
normtex(const float _val, const float len)
{
const float val = (_val < 0.0f) ? 0.0f : (_val > len) ? len : _val;
return (val / len);
}
static int
DrawVerts(SDL_Renderer * renderer, const SDL_FPoint * points, int count,
const MTLPrimitiveType primtype)
{
const size_t vertlen = (sizeof (float) * 2) * count;
float *verts = SDL_malloc(vertlen);
if (!verts) {
return SDL_OutOfMemory();
}
Dec 8, 2017
Dec 8, 2017
556
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
557
558
559
560
// !!! FIXME: render color should live in a dedicated uniform buffer.
const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
Dec 8, 2017
Dec 8, 2017
561
562
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
Dec 8, 2017
Dec 8, 2017
564
565
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
566
567
568
569
570
571
572
573
// !!! FIXME: we can convert this in the shader. This will save the malloc and for-loop, but we still need to upload.
float *ptr = verts;
for (int i = 0; i < count; i++, points++) {
*ptr = norm(points->x, w); ptr++;
*ptr = normy(points->y, h); ptr++;
}
Dec 8, 2017
Dec 8, 2017
574
575
[data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
[data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
SDL_free(verts);
return 0;
}
static int
METAL_RenderDrawPoints(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
{
return DrawVerts(renderer, points, count, MTLPrimitiveTypePoint);
}
static int
METAL_RenderDrawLines(SDL_Renderer * renderer, const SDL_FPoint * points, int count)
{
return DrawVerts(renderer, points, count, MTLPrimitiveTypeLineStrip);
}
static int
METAL_RenderFillRects(SDL_Renderer * renderer, const SDL_FRect * rects, int count)
{
Dec 8, 2017
Dec 8, 2017
596
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
597
598
599
600
// !!! FIXME: render color should live in a dedicated uniform buffer.
const float color[4] = { ((float)renderer->r) / 255.0f, ((float)renderer->g) / 255.0f, ((float)renderer->b) / 255.0f, ((float)renderer->a) / 255.0f };
Dec 8, 2017
Dec 8, 2017
601
602
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
Dec 8, 2017
Dec 8, 2017
604
605
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
606
607
608
609
610
611
612
613
614
615
616
617
for (int i = 0; i < count; i++, rects++) {
if ((rects->w <= 0.0f) || (rects->h <= 0.0f)) continue;
const float verts[] = {
norm(rects->x, w), normy(rects->y + rects->h, h),
norm(rects->x, w), normy(rects->y, h),
norm(rects->x + rects->w, w), normy(rects->y, h),
norm(rects->x, w), normy(rects->y + rects->h, h),
norm(rects->x + rects->w, w), normy(rects->y + rects->h, h)
};
Dec 8, 2017
Dec 8, 2017
618
619
[data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
[data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
620
621
622
623
624
625
626
627
628
}
return 0;
}
static int
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect)
{
Dec 8, 2017
Dec 8, 2017
629
630
631
632
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
const float texw = (float) mtltexture.width;
const float texh = (float) mtltexture.height;
const float xy[] = {
norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
norm(dstrect->x, w), normy(dstrect->y, h),
norm(dstrect->x + dstrect->w, w), normy(dstrect->y, h),
norm(dstrect->x, w), normy(dstrect->y + dstrect->h, h),
norm(dstrect->x + dstrect->w, w), normy(dstrect->y + dstrect->h, h)
};
const float uv[] = {
normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
normtex(srcrect->x, texw), normtex(srcrect->y, texh),
normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y, texh),
normtex(srcrect->x, texw), normtex(srcrect->y + srcrect->h, texh),
normtex(srcrect->x + srcrect->w, texw), normtex(srcrect->y + srcrect->h, texh)
};
float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
if (texture->modMode) {
color[0] = ((float)texture->r) / 255.0f;
color[1] = ((float)texture->g) / 255.0f;
color[2] = ((float)texture->b) / 255.0f;
color[3] = ((float)texture->a) / 255.0f;
}
Dec 8, 2017
Dec 8, 2017
660
661
662
663
664
665
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelinecopy, texture->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
[data.mtlcmdencoder setFragmentTexture:mtltexture atIndex:0];
[data.mtlcmdencoder setVertexBytes:xy length:sizeof(xy) atIndex:0];
[data.mtlcmdencoder setVertexBytes:uv length:sizeof(uv) atIndex:1];
[data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
return 0;
}
static int
METAL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect,
const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
{
return SDL_Unsupported(); // !!! FIXME
}
static int
METAL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 pixel_format, void * pixels, int pitch)
{
Dec 8, 2017
Dec 8, 2017
682
683
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
id<MTLTexture> mtltexture = colorAttachment.texture;
MTLRegion mtlregion;
mtlregion.origin.x = rect->x;
mtlregion.origin.y = rect->y;
mtlregion.origin.z = 0;
mtlregion.size.width = rect->w;
mtlregion.size.height = rect->w;
mtlregion.size.depth = 1;
// we only do BGRA8 or RGBA8 at the moment, so 4 will do.
const int temp_pitch = rect->w * 4;
void *temp_pixels = SDL_malloc(temp_pitch * rect->h);
if (!temp_pixels) {
return SDL_OutOfMemory();
}
[mtltexture getBytes:temp_pixels bytesPerRow:temp_pitch fromRegion:mtlregion mipmapLevel:0];
const Uint32 temp_format = (mtltexture.pixelFormat == MTLPixelFormatBGRA8Unorm) ? SDL_PIXELFORMAT_ARGB8888 : SDL_PIXELFORMAT_ABGR8888;
const int status = SDL_ConvertPixels(rect->w, rect->h, temp_format, temp_pixels, temp_pitch, pixel_format, pixels, pitch);
SDL_free(temp_pixels);
return status;
}
static void
METAL_RenderPresent(SDL_Renderer * renderer)
{
Dec 8, 2017
Dec 8, 2017
712
713
714
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
id<CAMetalDrawable> mtlbackbuffer = data.mtlbackbuffer;
Dec 8, 2017
Dec 8, 2017
716
717
[data.mtlcmdencoder endEncoding];
[data.mtlcmdbuffer presentDrawable:mtlbackbuffer];
Dec 8, 2017
Dec 8, 2017
719
720
[data.mtlcmdbuffer addCompletedHandler:^(id <MTLCommandBuffer> mtlcmdbuffer) {
#if !__has_feature(objc_arc)
721
[mtlbackbuffer release];
Dec 8, 2017
Dec 8, 2017
722
#endif
Dec 8, 2017
Dec 8, 2017
725
[data.mtlcmdbuffer commit];
726
727
728
// Start next frame, once we can.
// we don't specify a depth or stencil buffer because the render API doesn't currently use them.
Dec 8, 2017
Dec 8, 2017
729
730
731
data.mtlbackbuffer = [data.mtllayer nextDrawable];
SDL_assert(data.mtlbackbuffer);
colorAttachment.texture = data.mtlbackbuffer.texture;
732
colorAttachment.loadAction = MTLLoadActionDontCare;
Dec 8, 2017
Dec 8, 2017
733
734
735
data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
data.mtlcmdencoder.label = @"SDL metal renderer frame";
736
737
738
739
740
741
742
743
744
// Set up our current renderer state for the next frame...
METAL_UpdateViewport(renderer);
METAL_UpdateClipRect(renderer);
}
static void
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
{
Dec 8, 2017
Dec 8, 2017
745
746
id<MTLTexture> mtltexture = CFBridgingRelease(texture->driverdata);
#if !__has_feature(objc_arc)
747
[mtltexture release];
Dec 8, 2017
Dec 8, 2017
748
#endif
749
750
751
752
753
754
texture->driverdata = NULL;
}
static void
METAL_DestroyRenderer(SDL_Renderer * renderer)
{
Dec 8, 2017
Dec 8, 2017
755
756
if (renderer->driverdata) {
METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
Dec 8, 2017
Dec 8, 2017
758
#if !__has_feature(objc_arc)
Dec 8, 2017
Dec 8, 2017
760
761
762
763
[data.mtlcmdencoder endEncoding];
[data.mtlcmdencoder release];
[data.mtlcmdbuffer release];
[data.mtlcmdqueue release];
764
for (i = 0; i < 4; i++) {
Dec 8, 2017
Dec 8, 2017
765
766
[data.mtlpipelineprims[i] release];
[data.mtlpipelinecopy[i] release];
Dec 8, 2017
Dec 8, 2017
768
769
770
771
772
773
774
[data.mtlbufclearverts release];
[data.mtllibrary release];
[data.mtldevice release];
[data.mtlpassdesc release];
[data.mtllayer release];
[data release];
#endif
775
776
777
778
779
780
781
}
SDL_free(renderer);
}
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
/* vi: set ts=4 sw=4 expandtab: */