Skip to content

Latest commit

 

History

History
824 lines (701 loc) · 33.3 KB

SDL_render_metal.m

File metadata and controls

824 lines (701 loc) · 33.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
/* 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);
Dec 8, 2017
Dec 8, 2017
84
85
static void *METAL_GetMetalLayer(SDL_Renderer * renderer);
static void *METAL_GetMetalCommandEncoder(SDL_Renderer * renderer);
86
87
88
89
90
91
92
93
SDL_RenderDriver METAL_RenderDriver = {
METAL_CreateRenderer,
{
"metal",
(SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
2,
{SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888},
Dec 9, 2017
Dec 9, 2017
94
95
96
97
98
99
// !!! FIXME: how do you query Metal for this?
// (the weakest GPU supported by Metal on iOS has 4k texture max, and
// other models might be 2x or 4x more. On macOS, it's 16k across the
// board right now.)
4096,
100
101
102
4096}
};
Dec 8, 2017
Dec 8, 2017
103
@interface METAL_RenderData : NSObject
Dec 8, 2017
Dec 8, 2017
104
105
106
107
108
109
110
111
112
113
114
115
@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
116
117
118
119
@end
@implementation METAL_RenderData
@end
120
121
122
123
static int
IsMetalAvailable(const SDL_SysWMinfo *syswm)
{
Dec 8, 2017
Dec 8, 2017
124
125
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.");
126
127
128
}
// this checks a weak symbol.
Dec 8, 2017
Dec 8, 2017
129
#if (defined(__MACOSX__) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101100))
130
131
132
133
134
135
136
137
138
139
140
141
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
142
143
id<MTLFunction> mtlvertfn = [data.mtllibrary newFunctionWithName:vertfn];
id<MTLFunction> mtlfragfn = [data.mtllibrary newFunctionWithName:fragfn];
144
145
146
147
148
149
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
150
mtlpipedesc.colorAttachments[0].pixelFormat = data.mtllayer.pixelFormat;
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
182
183
184
185
default:
mtlpipedesc.colorAttachments[0].blendingEnabled = NO;
break;
186
187
188
189
190
}
mtlpipedesc.label = label;
NSError *err = nil;
Dec 8, 2017
Dec 8, 2017
191
id<MTLRenderPipelineState> retval = [data.mtldevice newRenderPipelineStateWithDescriptor:mtlpipedesc error:&err];
192
SDL_assert(err == nil);
Dec 8, 2017
Dec 8, 2017
193
#if !__has_feature(objc_arc)
194
195
196
197
[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
198
#endif
199
200
201
202
return retval;
}
static void
Dec 8, 2017
Dec 8, 2017
203
MakePipelineStates(METAL_RenderData *data, NSMutableArray *states,
204
205
NSString *label, NSString *vertfn, NSString *fragfn)
{
Dec 8, 2017
Dec 8, 2017
206
207
208
209
[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)];
210
211
212
}
static inline id<MTLRenderPipelineState>
Dec 8, 2017
Dec 8, 2017
213
ChoosePipelineState(NSMutableArray *states, const SDL_BlendMode blendmode)
214
215
{
switch (blendmode) {
Dec 8, 2017
Dec 8, 2017
216
217
218
219
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];
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
}
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
246
data = [[METAL_RenderData alloc] init];
Dec 8, 2017
Dec 8, 2017
247
data.beginScene = YES;
Dec 8, 2017
Dec 8, 2017
248
249
250
251
#if __has_feature(objc_arc)
renderer->driverdata = (void*)CFBridgingRetain(data);
#else
252
renderer->driverdata = data;
Dec 8, 2017
Dec 8, 2017
253
#endif
254
255
renderer->window = window;
Dec 8, 2017
Dec 8, 2017
256
257
258
#ifdef __MACOSX__
id<MTLDevice> mtldevice = MTLCreateSystemDefaultDevice(); // !!! FIXME: MTLCopyAllDevices() can find other GPUs...
if (mtldevice == nil) {
259
SDL_free(renderer);
Dec 8, 2017
Dec 8, 2017
260
261
262
#if !__has_feature(objc_arc)
[data release];
#endif
263
264
265
266
267
268
269
270
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
271
272
// CAMetalLayer is available in QuartzCore starting at OSX 10.11
CAMetalLayer *layer = [NSClassFromString( @"CAMetalLayer" ) layer];
Dec 8, 2017
Dec 8, 2017
274
layer.device = mtldevice;
275
276
277
278
279
280
281
282
283
//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
284
#else
Dec 8, 2017
Dec 8, 2017
285
286
UIView *view = UIKit_Mtl_AddMetalView(window);
CAMetalLayer *layer = (CAMetalLayer *)[view layer];
Dec 8, 2017
Dec 8, 2017
287
288
#endif
Dec 8, 2017
Dec 8, 2017
289
290
291
292
data.mtldevice = layer.device;
data.mtllayer = layer;
data.mtlcmdqueue = [data.mtldevice newCommandQueue];
data.mtlcmdqueue.label = @"SDL Metal Renderer";
Dec 9, 2017
Dec 9, 2017
293
data.mtlpassdesc = [MTLRenderPassDescriptor renderPassDescriptor];
Dec 8, 2017
Dec 8, 2017
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
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
312
313
314
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
316
// !!! FIXME: force more clears here so all the drawables are sane to start, and our static buffers are definitely flushed.
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;
Dec 8, 2017
Dec 8, 2017
338
339
renderer->GetMetalLayer = METAL_GetMetalLayer;
renderer->GetMetalCommandEncoder = METAL_GetMetalCommandEncoder;
340
341
342
343
344
345
346
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
347
348
return renderer;
}
Dec 8, 2017
Dec 8, 2017
350
351
352
static void METAL_ActivateRenderer(SDL_Renderer * renderer)
{
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
354
355
356
357
358
359
360
361
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];
Dec 9, 2017
Dec 9, 2017
362
data.mtlcmdencoder.label = @"SDL metal renderer start of frame";
Dec 8, 2017
Dec 8, 2017
363
364
365
366
367
// Set up our current renderer state for the next frame...
METAL_UpdateViewport(renderer);
METAL_UpdateClipRect(renderer);
}
368
369
370
371
372
373
374
375
376
377
378
379
380
381
}
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
382
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
383
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
384
385
386
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
388
}}
389
390
391
static int
METAL_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture)
Dec 8, 2017
Dec 8, 2017
392
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
393
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
394
395
396
397
398
399
400
401
402
403
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));
}
MTLTextureDescriptor *mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:mtlpixfmt
width:(NSUInteger)texture->w height:(NSUInteger)texture->h mipmapped:NO];
Dec 9, 2017
Dec 9, 2017
404
405
406
407
408
409
410
411
412
if (texture->access == SDL_TEXTUREACCESS_TARGET) {
mtltexdesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget;
} else {
mtltexdesc.usage = MTLTextureUsageShaderRead;
}
//mtltexdesc.resourceOptions = MTLResourceCPUCacheModeDefaultCache | MTLResourceStorageModeManaged;
//mtltexdesc.storageMode = MTLStorageModeManaged;
Dec 8, 2017
Dec 8, 2017
413
id<MTLTexture> mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
414
415
416
417
if (mtltexture == nil) {
return SDL_SetError("Texture allocation failed");
}
Dec 8, 2017
Dec 8, 2017
418
texture->driverdata = (void*)CFBridgingRetain(mtltexture);
419
420
return 0;
Dec 8, 2017
Dec 8, 2017
421
}}
422
423
424
425
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
426
{ @autoreleasepool {
427
428
429
430
// !!! 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
431
id<MTLTexture> mtltexture = (__bridge id<MTLTexture>) texture->driverdata;
432
433
[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
434
}}
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
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
461
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
462
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
463
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 9, 2017
Dec 9, 2017
464
465
466
467
468
469
470
// commit the current command buffer, so that any work on a render target
// will be available to the next one we're about to queue up.
[data.mtlcmdencoder endEncoding];
[data.mtlcmdbuffer commit];
id<MTLTexture> mtltexture = texture ? (__bridge id<MTLTexture>) texture->driverdata : data.mtlbackbuffer.texture;
Dec 8, 2017
Dec 8, 2017
471
data.mtlpassdesc.colorAttachments[0].texture = mtltexture;
Dec 9, 2017
Dec 9, 2017
472
473
474
475
476
477
478
479
// !!! FIXME: this can be MTLLoadActionDontCare for textures (not the backbuffer) if SDL doesn't guarantee the texture contents should survive.
data.mtlpassdesc.colorAttachments[0].loadAction = MTLLoadActionLoad;
data.mtlcmdbuffer = [data.mtlcmdqueue commandBuffer];
data.mtlcmdencoder = [data.mtlcmdbuffer renderCommandEncoderWithDescriptor:data.mtlpassdesc];
data.mtlcmdencoder.label = texture ? @"SDL metal renderer render texture" : @"SDL metal renderer backbuffer";
// The higher level will reset the viewport and scissor after this call returns.
Dec 8, 2017
Dec 8, 2017
481
}}
482
483
484
static int
METAL_UpdateViewport(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
485
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
486
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
487
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
488
489
490
491
492
493
494
495
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
497
}}
498
499
500
static int
METAL_UpdateClipRect(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
501
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
502
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
503
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
504
MTLScissorRect mtlrect;
Dec 8, 2017
Dec 8, 2017
505
// !!! FIXME: should this care about the viewport?
Dec 8, 2017
Dec 8, 2017
506
507
508
509
510
511
512
513
514
515
516
517
518
519
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];
520
521
}
return 0;
Dec 8, 2017
Dec 8, 2017
522
}}
523
524
525
static int
METAL_RenderClear(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
526
{ @autoreleasepool {
527
// 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
528
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
529
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
530
531
532
533
534
535
// !!! 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 9, 2017
Dec 9, 2017
536
537
viewport.width = data.mtlpassdesc.colorAttachments[0].texture.width;
viewport.height = data.mtlpassdesc.colorAttachments[0].texture.height;
538
539
540
541
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
542
543
544
545
546
[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];
547
548
549
550
551
552
553
554
// 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
555
[data.mtlcmdencoder setViewport:viewport];
556
557
return 0;
Dec 8, 2017
Dec 8, 2017
558
}}
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
// 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
586
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
587
588
METAL_ActivateRenderer(renderer);
589
590
591
592
593
594
const size_t vertlen = (sizeof (float) * 2) * count;
float *verts = SDL_malloc(vertlen);
if (!verts) {
return SDL_OutOfMemory();
}
Dec 8, 2017
Dec 8, 2017
595
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
596
597
598
599
// !!! 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
600
601
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
Dec 8, 2017
Dec 8, 2017
603
604
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
605
606
607
608
609
610
611
612
// !!! 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
613
614
[data.mtlcmdencoder setVertexBytes:verts length:vertlen atIndex:0];
[data.mtlcmdencoder drawPrimitives:primtype vertexStart:0 vertexCount:count];
615
616
617
SDL_free(verts);
return 0;
Dec 8, 2017
Dec 8, 2017
618
}}
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
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
634
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
635
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
636
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
637
638
639
640
// !!! 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
641
642
[data.mtlcmdencoder setRenderPipelineState:ChoosePipelineState(data.mtlpipelineprims, renderer->blendMode)];
[data.mtlcmdencoder setFragmentBytes:color length:sizeof(color) atIndex:0];
Dec 8, 2017
Dec 8, 2017
644
645
const float w = (float) data.mtlpassdesc.colorAttachments[0].texture.width;
const float h = (float) data.mtlpassdesc.colorAttachments[0].texture.height;
646
647
648
649
650
651
652
653
654
655
656
657
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
658
659
[data.mtlcmdencoder setVertexBytes:verts length:sizeof(verts) atIndex:0];
[data.mtlcmdencoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:5];
660
661
662
}
return 0;
Dec 8, 2017
Dec 8, 2017
663
}}
664
665
666
667
static int
METAL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect)
Dec 8, 2017
Dec 8, 2017
668
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
669
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
670
671
672
673
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;
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
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
701
702
703
704
705
706
[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];
707
708
return 0;
Dec 8, 2017
Dec 8, 2017
709
}}
710
711
712
713
714
715
716
717
718
719
720
721
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
722
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
723
METAL_ActivateRenderer(renderer);
Dec 9, 2017
Dec 9, 2017
724
// !!! FIXME: this probably needs to commit the current command buffer, and probably waitUntilCompleted
Dec 8, 2017
Dec 8, 2017
725
726
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
MTLRenderPassColorAttachmentDescriptor *colorAttachment = data.mtlpassdesc.colorAttachments[0];
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
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
750
}}
751
752
753
static void
METAL_RenderPresent(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
754
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
755
METAL_ActivateRenderer(renderer);
Dec 8, 2017
Dec 8, 2017
756
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
Dec 8, 2017
Dec 8, 2017
758
[data.mtlcmdencoder endEncoding];
Dec 8, 2017
Dec 8, 2017
759
[data.mtlcmdbuffer presentDrawable:data.mtlbackbuffer];
Dec 8, 2017
Dec 8, 2017
760
[data.mtlcmdbuffer commit];
Dec 8, 2017
Dec 8, 2017
761
762
763
data.mtlcmdencoder = nil;
data.mtlcmdbuffer = nil;
data.mtlbackbuffer = nil;
Dec 8, 2017
Dec 8, 2017
764
data.beginScene = YES;
Dec 8, 2017
Dec 8, 2017
765
}}
766
767
768
static void
METAL_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture)
Dec 8, 2017
Dec 8, 2017
769
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
770
771
id<MTLTexture> mtltexture = CFBridgingRelease(texture->driverdata);
#if !__has_feature(objc_arc)
772
[mtltexture release];
Dec 8, 2017
Dec 8, 2017
773
#endif
774
texture->driverdata = NULL;
Dec 8, 2017
Dec 8, 2017
775
}}
776
777
778
static void
METAL_DestroyRenderer(SDL_Renderer * renderer)
Dec 8, 2017
Dec 8, 2017
779
{ @autoreleasepool {
Dec 8, 2017
Dec 8, 2017
780
781
if (renderer->driverdata) {
METAL_RenderData *data = CFBridgingRelease(renderer->driverdata);
Dec 8, 2017
Dec 8, 2017
783
784
785
if (data.mtlcmdencoder != nil) {
[data.mtlcmdencoder endEncoding];
}
Dec 8, 2017
Dec 8, 2017
786
787
788
789
790
#if !__has_feature(objc_arc)
[data.mtlbackbuffer release];
[data.mtlcmdencoder release];
[data.mtlcmdbuffer release];
Dec 8, 2017
Dec 8, 2017
791
[data.mtlcmdqueue release];
Dec 8, 2017
Dec 8, 2017
792
for (int i = 0; i < 4; i++) {
Dec 8, 2017
Dec 8, 2017
793
794
[data.mtlpipelineprims[i] release];
[data.mtlpipelinecopy[i] release];
Dec 8, 2017
Dec 8, 2017
796
797
[data.mtlpipelineprims release];
[data.mtlpipelinecopy release];
Dec 8, 2017
Dec 8, 2017
798
799
800
801
802
803
[data.mtlbufclearverts release];
[data.mtllibrary release];
[data.mtldevice release];
[data.mtlpassdesc release];
[data.mtllayer release];
#endif
Dec 8, 2017
Dec 8, 2017
805
806
SDL_free(renderer);
Dec 8, 2017
Dec 8, 2017
807
}}
Dec 8, 2017
Dec 8, 2017
809
810
811
812
813
814
815
816
817
818
819
820
821
void *METAL_GetMetalLayer(SDL_Renderer * renderer)
{ @autoreleasepool {
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
return (__bridge void*)data.mtllayer;
}}
void *METAL_GetMetalCommandEncoder(SDL_Renderer * renderer)
{ @autoreleasepool {
METAL_ActivateRenderer(renderer);
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
return (__bridge void*)data.mtlcmdencoder;
}}
822
823
824
#endif /* SDL_VIDEO_RENDER_METAL && !SDL_RENDER_DISABLED */
/* vi: set ts=4 sw=4 expandtab: */