Skip to content

Latest commit

 

History

History
791 lines (678 loc) · 31.5 KB

SDL_render_metal.m

File metadata and controls

791 lines (678 loc) · 31.5 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
@interface METAL_RenderData : NSObject
Dec 8, 2017
Dec 8, 2017
97
98
99
100
101
102
103
104
105
106
107
108
@property (nonatomic, assign) BOOL beginScene;
@property (nonatomic, retain) id<MTLDevice> mtldevice;
@property (nonatomic, retain) id<MTLCommandQueue> mtlcmdqueue;
@property (nonatomic, retain) id<MTLCommandBuffer> mtlcmdbuffer;
@property (nonatomic, retain) id<MTLRenderCommandEncoder> mtlcmdencoder;
@property (nonatomic, retain) id<MTLLibrary> mtllibrary;
@property (nonatomic, retain) id<CAMetalDrawable> mtlbackbuffer;
@property (nonatomic, retain) NSMutableArray *mtlpipelineprims;
@property (nonatomic, retain) NSMutableArray *mtlpipelinecopy;
@property (nonatomic, retain) id<MTLBuffer> mtlbufclearverts;
@property (nonatomic, retain) CAMetalLayer *mtllayer;
@property (nonatomic, retain) MTLRenderPassDescriptor *mtlpassdesc;
Dec 8, 2017
Dec 8, 2017
109
110
111
112
@end
@implementation METAL_RenderData
@end
113
114
115
116
static int
IsMetalAvailable(const SDL_SysWMinfo *syswm)
{
Dec 8, 2017
Dec 8, 2017
117
118
if (syswm->subsystem != SDL_SYSWM_COCOA && syswm->subsystem != SDL_SYSWM_UIKIT) {
return SDL_SetError("Metal render target only supports Cocoa and UIKit video targets at the moment.");
119
120
121
}
// this checks a weak symbol.
Dec 8, 2017
Dec 8, 2017
122
#if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
123
124
125
126
127
128
129
130
131
132
133
134
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
135
136
id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:vertfn];
id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:fragfn];
137
138
139
140
141
142
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
143
mtlpipedesc.colorAttachments[0].pixelFormat = data.mtllayer.pixelFormat;
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
174
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
175
176
177
178
default:
mtlpipedesc.colorAttachments[0].blendingEnabled = NO;
break;
179
180
181
182
183
}
mtlpipedesc.label = label;
NSError *err = nil;
Dec 8, 2017
Dec 8, 2017
184
id<MTLRenderPipelineState> retval = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
185
SDL_assert(err == nil);
Dec 8, 2017
Dec 8, 2017
186
#if !__has_feature(objc_arc)
187
188
189
190
[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
191
#endif
192
193
194
195
return retval;
}
static void
Dec 8, 2017
Dec 8, 2017
196
MakePipelineStates(METAL_RenderData *data, NSMutableArray *states,
197
198
NSString *label, NSString *vertfn, NSString *fragfn)
{
Dec 8, 2017
Dec 8, 2017
199
200
201
202
[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)];
203
204
205
}
static inline id<MTLRenderPipelineState>
Dec 8, 2017
Dec 8, 2017
206
ChoosePipelineState(NSMutableArray *states, const SDL_BlendMode blendmode)
207
208
{
switch (blendmode) {
Dec 8, 2017
Dec 8, 2017
209
210
211
212
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];
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
238
}
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
239
data = [[METAL_RenderData alloc] init];
Dec 8, 2017
Dec 8, 2017
240
data.beginScene = YES;
Dec 8, 2017
Dec 8, 2017
241
242
243
244
#if __has_feature(objc_arc)
renderer->driverdata = (void*)CFBridgingRetain(data);
#else
245
renderer->driverdata = data;
Dec 8, 2017
Dec 8, 2017
246
#endif
247
248
renderer->window = window;
Dec 8, 2017
Dec 8, 2017
249
250
251
#ifdef __MACOSX__
id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice(); // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
if (mtldevice == nil) {
252
SDL_free(renderer);
Dec 8, 2017
Dec 8, 2017
253
254
255
#if !__has_feature(objc_arc)
[data release];
#endif
256
257
258
259
260
261
262
263
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
264
265
// CAMetalLayer is available in QuartzCore starting at OSX 10.11
CAMetalLayer *layer = [NSClassFromString( @"CAMetalLayer" ) layer];
Dec 8, 2017
Dec 8, 2017
267
layer.device = mtldevice;
268
269
270
271
272
273
274
275
276
//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
277
#else
Dec 8, 2017
Dec 8, 2017
278
279
UIView *view = UIKit_Mtl_AddMetalView(window);
CAMetalLayer *layer = (CAMetalLayer *)[view layer];
Dec 8, 2017
Dec 8, 2017
280
281
#endif
Dec 8, 2017
Dec 8, 2017
282
283
284
285
286
data.mtldevice = layer.device;
data.mtllayer = layer;
data.mtlcmdqueue = [data.mtldevice newCommandQueue];
data.mtlcmdqueue.label = @"SDL Metal Renderer";
data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor]; // !!! FIXME: is this autoreleased?
Dec 8, 2017
Dec 8, 2017
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
NSError *err = nil;
// 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.
dispatch_data_t mtllibdata = dispatch_data_create(sdl_metallib, sdl_metallib_len, dispatch_get_global_queue(0, 0), ^{});
data.mtllibrary = [data.mtldevice newLibraryWithData:mtllibdata error:&err];
SDL_assert(err == nil);
#if !__has_feature(objc_arc)
dispatch_release(mtllibdata);
#endif
data.mtllibrary.label = @"SDL Metal renderer shader library";
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");
Dec 8, 2017
Dec 8, 2017
305
306
307
static const float clearverts[] = { -1, -1, -1, 1, 1, 1, 1, -1, -1, -1 };
data.mtlbufclearverts = [data.mtldevice newBufferWithBytes:clearverts length:sizeof(clearverts) options:MTLResourceCPUCacheModeWriteCombined];
data.mtlbufclearverts.label = @"SDL_RenderClear vertices";
Dec 8, 2017
Dec 8, 2017
309
// !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
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;
Dec 8, 2017
Dec 8, 2017
338
339
return renderer;
}
Dec 8, 2017
Dec 8, 2017
341
342
343
static void METAL_ActivateRenderer(SDL_Renderer * renderer)
{
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
345
346
347
348
349
350
351
352
353
354
355
356
357
358
if (data.beginScene) {
data.beginScene = NO;
data.mtlbackbuffer = [data.mtllayer nextDrawable];
SDL_assert(data.mtlbackbuffer);
data.mtlpassdesc.colorAttachments[0].texture = data.mtlbackbuffer.texture;
data.mtlpassdesc.colorAttachments[0].loadAction = MTLLoadActionDontCare;
data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
data.mtlcmdencoder.label = @"SDL metal renderer frame";
// Set up our current renderer state for the next frame...
METAL_UpdateViewport(renderer);
METAL_UpdateClipRect(renderer);
}
359
360
361
362
363
364
365
366
367
368
369
370
371
372
}
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
373
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
374
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
375
376
377
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
*w = (int) data.mtlbackbuffer.texture.width;
*h = (int) data.mtlbackbuffer.texture.height;
Dec 8, 2017
Dec 8, 2017
379
}}
380
381
382
static int
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
Dec 8, 2017
Dec 8, 2017
383
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
384
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
385
386
387
388
389
390
391
392
393
394
395
396
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
397
398
id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
#if !__has_feature(objc_arc)
399
[mtltexdesc release];
Dec 8, 2017
Dec 8, 2017
400
#endif
401
402
403
404
if (mtltexture == nil) {
return SDL_SetError("Texture allocation failed");
}
Dec 8, 2017
Dec 8, 2017
405
texture->driverdata = (void*)CFBridgingRetain(mtltexture);
406
407
return 0;
Dec 8, 2017
Dec 8, 2017
408
}}
409
410
411
412
static int
METAL_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels, int pitch)
Dec 8, 2017
Dec 8, 2017
413
{ @autoreleasepool {
414
415
416
417
// !!! 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
418
id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
419
420
[mtltexture replaceRegion:MTLRegionMake2D(rect->x, rect->y, rect->w, rect->h) mipmapLevel:0 withBytes:pixels bytesPerRow:pitch];
return 0;
Dec 8, 2017
Dec 8, 2017
421
}}
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
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
448
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
449
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
450
451
452
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
id<MTLTexture> mtltexture = texture ? (__bridge id<MTLTexture>) texture->driverdata : nil;
data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
Dec 8, 2017
Dec 8, 2017
454
}}
455
456
457
static int
METAL_UpdateViewport(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
458
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
459
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
460
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
461
462
463
464
465
466
467
468
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;
[data.mtlcmdencoder setViewport:viewport];
Dec 8, 2017
Dec 8, 2017
470
}}
471
472
473
static int
METAL_UpdateClipRect(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
474
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
475
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
476
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
477
MTLScissorRect mtlrect;
Dec 8, 2017
Dec 8, 2017
478
// !!! FIXME: should this care about the viewport?
Dec 8, 2017
Dec 8, 2017
479
480
481
482
483
484
485
486
487
488
489
490
491
492
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;
}
if (mtlrect.width > 0 && mtlrect.height > 0) {
[data.mtlcmdencoder setScissorRect:mtlrect];
493
494
}
return 0;
Dec 8, 2017
Dec 8, 2017
495
}}
496
497
498
static int
METAL_RenderClear(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
499
{ @autoreleasepool {
500
// 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
501
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
502
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
503
504
505
506
507
508
// !!! 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
509
510
viewport.width = data.mtlbackbuffer.texture.width;
viewport.height = data.mtlbackbuffer.texture.height;
511
512
513
514
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
515
516
517
518
519
[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];
520
521
522
523
524
525
526
527
// 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
528
[data.mtlcmdencoder setViewport:viewport];
529
530
return 0;
Dec 8, 2017
Dec 8, 2017
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
556
557
558
// 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)
Dec 8, 2017
Dec 8, 2017
559
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
560
561
METAL_ActivateRenderer(renderer);
562
563
564
565
566
567
const size_t vertlen = (sizeof (float) * 2) * count;
float *verts = SDL_malloc(vertlen);
if (!verts) {
return SDL_OutOfMemory();
}
Dec 8, 2017
Dec 8, 2017
568
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
569
570
571
572
// !!! 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
573
574
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
Dec 8, 2017
Dec 8, 2017
576
577
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
578
579
580
581
582
583
584
585
// !!! 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
586
587
[data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
[data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
588
589
590
SDL_free(verts);
return 0;
Dec 8, 2017
Dec 8, 2017
591
}}
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
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
607
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
608
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
609
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
610
611
612
613
// !!! 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
614
615
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
Dec 8, 2017
Dec 8, 2017
617
618
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
619
620
621
622
623
624
625
626
627
628
629
630
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
631
632
[data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
[data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
633
634
635
}
return 0;
Dec 8, 2017
Dec 8, 2017
636
}}
637
638
639
640
static int
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect)
Dec 8, 2017
Dec 8, 2017
641
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
642
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
643
644
645
646
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;
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
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
674
675
676
677
678
679
[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];
680
681
return 0;
Dec 8, 2017
Dec 8, 2017
682
}}
683
684
685
686
687
688
689
690
691
692
693
694
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
695
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
696
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
697
698
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
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;
Dec 8, 2017
Dec 8, 2017
722
}}
723
724
725
static void
METAL_RenderPresent(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
726
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
727
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
728
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
730
[data.mtlcmdencoder endEncoding];
Dec 8, 2017
Dec 8, 2017
731
[data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
Dec 8, 2017
Dec 8, 2017
732
[data.mtlcmdbuffer commit];
Dec 8, 2017
Dec 8, 2017
733
734
735
#if !__has_feature(objc_arc)
[data.mtlcmdencoder release];
[data.mtlcmdbuffer release];
Dec 8, 2017
Dec 8, 2017
736
[data.mtlbackbuffer release];
Dec 8, 2017
Dec 8, 2017
737
#endif
Dec 8, 2017
Dec 8, 2017
738
739
740
data.mtlcmdencoder = nil;
data.mtlcmdbuffer = nil;
data.mtlbackbuffer = nil;
Dec 8, 2017
Dec 8, 2017
741
data.beginScene = YES;
Dec 8, 2017
Dec 8, 2017
742
}}
743
744
745
static void
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
Dec 8, 2017
Dec 8, 2017
746
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
747
748
id<MTLTexture> mtltexture = CFBridgingRelease(texture->driverdata);
#if !__has_feature(objc_arc)
749
[mtltexture release];
Dec 8, 2017
Dec 8, 2017
750
#endif
751
texture->driverdata = NULL;
Dec 8, 2017
Dec 8, 2017
752
}}
753
754
755
static void
METAL_DestroyRenderer(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
756
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
757
758
if (renderer->driverdata) {
METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
Dec 8, 2017
Dec 8, 2017
760
#if !__has_feature(objc_arc)
Dec 8, 2017
Dec 8, 2017
761
762
763
764
765
766
767
768
769
770
if (data.mtlbackbuffer != nil) {
[data.mtlbackbuffer release];
}
if (data.mtlcmdencoder != nil) {
[data.mtlcmdencoder endEncoding];
[data.mtlcmdencoder release];
}
if (data.mtlcmdbuffer != nil) {
[data.mtlcmdbuffer release];
}
Dec 8, 2017
Dec 8, 2017
771
[data.mtlcmdqueue release];
Dec 8, 2017
Dec 8, 2017
772
for (int i = 0; i < 4; i++) {
Dec 8, 2017
Dec 8, 2017
773
774
[data.mtlpipelineprims[i] release];
[data.mtlpipelinecopy[i] release];
Dec 8, 2017
Dec 8, 2017
776
777
[data.mtlpipelineprims release];
[data.mtlpipelinecopy release];
Dec 8, 2017
Dec 8, 2017
778
779
780
781
782
783
784
[data.mtlbufclearverts release];
[data.mtllibrary release];
[data.mtldevice release];
[data.mtlpassdesc release];
[data.mtllayer release];
[data release];
#endif
785
786
}
SDL_free(renderer);
Dec 8, 2017
Dec 8, 2017
787
}}
788
789
790
791
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
/* vi: set ts=4 sw=4 expandtab: */