Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8260931: Implement JEP 382: New macOS Rendering Pipeline #2403

Closed
wants to merge 47 commits into from
Closed
Changes from 9 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0b8d96b
Project Lanai Patch
aghaisas Feb 4, 2021
5476874
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Feb 8, 2021
8ed7b5f
Lanai PR#174 - 8261234 - kcr
aghaisas Feb 8, 2021
6044adc
Lanai PR#175 - 8261304 - aghaisas
aghaisas Feb 8, 2021
fdf3be9
Lanai PR#176 - 8261399 - jdv
aghaisas Feb 9, 2021
6417328
Lanai PR#177 - 8261430 - aghaisas
aghaisas Feb 9, 2021
9a72538
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Feb 11, 2021
9c13bb3
Lanai PR#178 - 8261273 - avu
aghaisas Feb 11, 2021
6a3f96e
Lanai PR#179 - 8261402 - avu
aghaisas Feb 11, 2021
102494b
Lanai PR#180 - 8261546 - jdv
aghaisas Feb 11, 2021
7340d06
Lanai PR#181 - 8261143 - aghaisas
aghaisas Feb 11, 2021
a7df89b
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Feb 15, 2021
edf247d
Lanai PR#182 - 8261547 - psadhukhan
aghaisas Feb 15, 2021
8b5bf34
Lanai PR#184 - 8261620 - aghaisas
aghaisas Feb 15, 2021
d2548e0
Lanai PR#185 - 8261632 - jdv
aghaisas Feb 15, 2021
0d8aedc
Lanai PR#186 - 8261638 - avu
aghaisas Feb 15, 2021
3942a5b
Lanai PR#187 - 8261704 - jdv
aghaisas Feb 15, 2021
90af90d
Lanai PR#189 - 8261712 - avu
aghaisas Feb 15, 2021
1c0c0c1
Lanai PR#190 - 8261706 - jdv
aghaisas Feb 15, 2021
7b0b0dc
Lanai PR#191 - 8261705 - jdv
aghaisas Feb 15, 2021
10ebb70
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Feb 21, 2021
9432c5a
Lanai PR#192 - 8261789 - aghaisas
aghaisas Feb 21, 2021
68220fb
Lanai PR#193 - 8261734 - avu
aghaisas Feb 21, 2021
30284b9
Lanai PR#194 - 8261703 - jdv
aghaisas Feb 21, 2021
e2f78dd
Lanai PR#195 - 8261908 - jdv
aghaisas Feb 21, 2021
1e0ff66
Lanai PR#196 - 8260715 - avu
aghaisas Feb 21, 2021
8c598a6
Lanai PR#197 - 8261960 - jdv
aghaisas Feb 21, 2021
a2983f0
Lanai PR#198 - 8261646 - avu
aghaisas Feb 21, 2021
614be05
Lanai PR#199 - 8262091 - aghaisas
aghaisas Feb 21, 2021
30dab8d
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Mar 1, 2021
bf888a2
Lanai PR#200 - 8262115 - aghaisas
aghaisas Mar 1, 2021
6e2e528
Lanai PR#201 - 8261891 - avu
aghaisas Mar 1, 2021
9059255
Lanai PR#202 - 8262293 - avu
aghaisas Mar 1, 2021
e0fbc42
Lanai PR#203 - 8262313 - jdv
aghaisas Mar 1, 2021
c9ebcbc
Lanai PR#205 - 8262496 - avu
aghaisas Mar 1, 2021
5cb1fd9
Lanai PR#206 - 8262729 - aghaisas
aghaisas Mar 1, 2021
09519fd
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Mar 8, 2021
3995be1
Lanai PR#207 - 8262750 - jdv
aghaisas Mar 8, 2021
5660744
Lanai PR#208 - 8262928 - jdv
aghaisas Mar 8, 2021
e459f1a
Lanai PR#209 - 8262936 - jdv
aghaisas Mar 8, 2021
de45693
Lanai PR#210 - 8263159 - jdv
aghaisas Mar 8, 2021
b065a49
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Mar 10, 2021
d27ff90
Lanai PR#211 - 8262882 - aghaisas
aghaisas Mar 10, 2021
9a27831
Lanai PR#212 - 8259825 - aghaisas
aghaisas Mar 10, 2021
3980ecb
Lanai PR#213 - 8263325 - avu
aghaisas Mar 10, 2021
c4309ab
Merge branch 'master' into 8260931_lanai_JEP_branch
aghaisas Mar 11, 2021
369c3d2
Lanai PR#214 - 8263324 - avu
aghaisas Mar 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -68,6 +68,10 @@
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque;

- (id<MTLRenderCommandEncoder> _Nonnull) getLCDEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque;

- (id<MTLRenderCommandEncoder> _Nonnull)getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
@@ -44,14 +44,16 @@ - (void)reset:(id<MTLTexture>)destination
isDstOpaque:(jboolean)isDstOpaque
isDstPremultiplied:(jboolean)isDstPremultiplied
isAA:(jboolean)isAA
isText:(jboolean)isText;
isText:(jboolean)isText
isLCD:(jboolean)isLCD;

- (void)updateEncoder:(id<MTLRenderCommandEncoder>)encoder
context:(MTLContext *)mtlc
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate;
@property (assign) jboolean aa;
@property (assign) jboolean text;
@property (assign) jboolean lcd;
@property (retain) MTLPaint* paint;
@end

@@ -65,6 +67,7 @@ @implementation EncoderStates {

jboolean _isAA;
jboolean _isText;
jboolean _isLCD;

//
// Cached 'mutable' states of encoder
@@ -89,6 +92,7 @@ @implementation EncoderStates {
}
@synthesize aa = _isAA;
@synthesize text = _isText;
@synthesize lcd = _isLCD;
@synthesize paint = _paint;

- (id)init {
@@ -119,12 +123,14 @@ - (void)reset:(id<MTLTexture>)destination
isDstOpaque:(jboolean)isDstOpaque
isDstPremultiplied:(jboolean)isDstPremultiplied
isAA:(jboolean)isAA
isText:(jboolean)isText {
isText:(jboolean)isText
isLCD:(jboolean)isLCD {
_destination = destination;
_dstFlags.isOpaque = isDstOpaque;
_dstFlags.isPremultiplied = isDstPremultiplied;
_isAA = isAA;
_isText = isText;
_isLCD = isLCD;
// NOTE: probably it's better to invalidate/reset all cached states now
}

@@ -173,6 +179,7 @@ - (void)updatePipelineState:(id<MTLRenderCommandEncoder>)encoder
&& (_isTexture == renderOptions->isTexture && (!renderOptions->isTexture || _interpolationMode == renderOptions->interpolation)) // interpolation is used only in texture mode
&& _isAA == renderOptions->isAA
&& _isText == renderOptions->isText
&& _isLCD == renderOptions->isLCD
&& _srcFlags.isOpaque == renderOptions->srcFlags.isOpaque && _srcFlags.isPremultiplied == renderOptions->srcFlags.isPremultiplied)
return;

@@ -182,6 +189,7 @@ - (void)updatePipelineState:(id<MTLRenderCommandEncoder>)encoder
_interpolationMode = renderOptions->interpolation;
_isAA = renderOptions->isAA;
_isText = renderOptions->isText;
_isLCD = renderOptions->isLCD;
_srcFlags = renderOptions->srcFlags;

if ((jint)[mtlc.composite getCompositeState] == sun_java2d_SunGraphics2D_COMP_XOR) {
@@ -275,14 +283,14 @@ - (void)setContext:(MTLContex * _Nonnull)mtlc {

- (id<MTLRenderCommandEncoder> _Nonnull)getAARenderEncoder:(const BMTLSDOps * _Nonnull)dstOps {
id<MTLTexture> dstTxt = dstOps->pTexture;
RenderOptions roptions = {JNI_FALSE, JNI_TRUE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}, JNI_FALSE};
RenderOptions roptions = {JNI_FALSE, JNI_TRUE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {dstOps->isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dstTxt renderOptions:&roptions];
}

- (id<MTLRenderCommandEncoder> _Nonnull)getRenderEncoder:(id<MTLTexture> _Nonnull)dest
isDstOpaque:(bool)isOpaque
{
RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {isOpaque, JNI_TRUE}, JNI_FALSE};
RenderOptions roptions = {JNI_FALSE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, defaultRasterFlags, {isOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dest renderOptions:&roptions];
}

@@ -306,13 +314,21 @@ - (void)setContext:(MTLContex * _Nonnull)mtlc {
isAA:JNI_FALSE];
}

- (id<MTLRenderCommandEncoder> _Nonnull) getLCDEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
{
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, {isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE, JNI_TRUE};
return [self getEncoder:dest renderOptions:&roptions];
}

- (id<MTLRenderCommandEncoder> _Nonnull) getTextureEncoder:(id<MTLTexture> _Nonnull)dest
isSrcOpaque:(bool)isSrcOpaque
isDstOpaque:(bool)isDstOpaque
interpolation:(int)interpolation
isAA:(jboolean)isAA
{
RenderOptions roptions = {JNI_TRUE, isAA, interpolation, { isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE};
RenderOptions roptions = {JNI_TRUE, isAA, interpolation, { isSrcOpaque, JNI_TRUE }, {isDstOpaque, JNI_TRUE}, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dest renderOptions:&roptions];
}

@@ -327,7 +343,7 @@ - (void)setContext:(MTLContex * _Nonnull)mtlc {
- (id<MTLRenderCommandEncoder> _Nonnull) getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
isSrcOpaque:(bool)isSrcOpaque
{
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE }, {dstOps->isOpaque, JNI_TRUE}, JNI_TRUE};
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE }, {dstOps->isOpaque, JNI_TRUE}, JNI_TRUE, JNI_FALSE};
return [self getEncoder:dstOps->pTexture renderOptions:&roptions];
}

@@ -407,7 +423,8 @@ - (void)setContext:(MTLContex * _Nonnull)mtlc {
isDstOpaque:renderOptions->dstFlags.isOpaque
isDstPremultiplied:YES
isAA:renderOptions->isAA
isText:renderOptions->isText];
isText:renderOptions->isText
isLCD:renderOptions->isLCD];
}

//
@@ -162,7 +162,8 @@ void drawTex2Tex(MTLContext *mtlc,
const int dw = dx2 - dx1;
const int dh = dy2 - dy1;
if (dw < sw || dh < sh) {
J2dTraceLn4(J2D_TRACE_WARNING, "replaceTextureRegion: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);
J2dTraceLn4(J2D_TRACE_ERROR, "replaceTextureRegion: dest size: (%d, %d) less than source size: (%d, %d)", dw, dh, sw, sh);
return;
}

const void *raster = srcInfo->rasBase;
@@ -213,6 +214,14 @@ void drawTex2Tex(MTLContext *mtlc,
toTexture:dest
destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
[blitEncoder endEncoding];
[mtlc.encoderManager endEncoder];

MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
}

@@ -752,27 +761,47 @@ void copyFromMTLBuffer(void *pDst, id<MTLBuffer> srcBuf, jint offset, jint len,
J2dTraceImpl(J2D_TRACE_VERBOSE, JNI_TRUE, "MTLBlitLoops_CopyArea: bdst=%p [tex=%p] %dx%d | src (%d, %d), %dx%d -> dst (%d, %d)",
dstOps, dstOps->pTexture, ((id<MTLTexture>)dstOps->pTexture).width, ((id<MTLTexture>)dstOps->pTexture).height, x, y, width, height, dx, dy);
#endif //DEBUG
jint texWidth = ((id<MTLTexture>)dstOps->pTexture).width;
jint texHeight = ((id<MTLTexture>)dstOps->pTexture).height;

SurfaceDataBounds srcBounds, dstBounds;
srcBounds.x1 = x;
srcBounds.y1 = y;
srcBounds.x2 = srcBounds.x1 + width;
srcBounds.y2 = srcBounds.y1 + height;
dstBounds.x1 = x + dx;
dstBounds.y1 = y + dy;
dstBounds.x2 = dstBounds.x1 + width;
dstBounds.y2 = dstBounds.y1 + height;

SurfaceData_IntersectBoundsXYXY(&srcBounds, 0, 0, texWidth, texHeight);
SurfaceData_IntersectBoundsXYXY(&dstBounds, 0, 0, texWidth, texHeight);
SurfaceData_IntersectBlitBounds(&dstBounds, &srcBounds, -dx, -dy);

int srcWidth = (srcBounds.x2 - srcBounds.x1);
int srcHeight = (srcBounds.y2 - srcBounds.y1);

if ((srcBounds.x1 < srcBounds.x2 && srcBounds.y1 < srcBounds.y2) &&
(dstBounds.x1 < dstBounds.x2 && dstBounds.y1 < dstBounds.y2))
{
@autoreleasepool {
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];

@autoreleasepool {
id<MTLCommandBuffer> cb = [mtlc createCommandBuffer];
id<MTLBlitCommandEncoder> blitEncoder = [cb blitCommandEncoder];

// Create an intrermediate buffer
int totalBuffsize = width * height * 4;
id <MTLBuffer> buff = [[mtlc.device newBufferWithLength:totalBuffsize options:MTLResourceStorageModePrivate] autorelease];
// Create an intrermediate buffer
int totalBuffsize = srcWidth * srcHeight * 4;
id <MTLBuffer> buff = [[mtlc.device newBufferWithLength:totalBuffsize options:MTLResourceStorageModePrivate] autorelease];

[blitEncoder copyFromTexture:dstOps->pTexture
sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(x, y, 0) sourceSize:MTLSizeMake(width, height, 1)
toBuffer:buff destinationOffset:0 destinationBytesPerRow:(width * 4) destinationBytesPerImage:totalBuffsize];
[blitEncoder copyFromTexture:dstOps->pTexture
sourceSlice:0 sourceLevel:0 sourceOrigin:MTLOriginMake(srcBounds.x1, srcBounds.y1, 0) sourceSize:MTLSizeMake(srcWidth, srcHeight, 1)
toBuffer:buff destinationOffset:0 destinationBytesPerRow:(srcWidth * 4) destinationBytesPerImage:totalBuffsize];

[blitEncoder copyFromBuffer:buff
sourceOffset:0 sourceBytesPerRow:width*4 sourceBytesPerImage:totalBuffsize sourceSize:MTLSizeMake(width, height, 1)
toTexture:dstOps->pTexture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(x + dx, y + dy, 0)];
[blitEncoder endEncoding];

[cb commit];
}
[blitEncoder copyFromBuffer:buff
sourceOffset:0 sourceBytesPerRow:srcWidth*4 sourceBytesPerImage:totalBuffsize sourceSize:MTLSizeMake(srcWidth, srcHeight, 1)
toTexture:dstOps->pTexture destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dstBounds.x1, dstBounds.y1, 0)];
[blitEncoder endEncoding];

// TODO:
// 1. check rect bounds
[cb commit];
}
}
}
@@ -272,7 +272,26 @@ - (void)setScissorOrStencil:(id<MTLRenderCommandEncoder>)encoder
_clipRect.height = dh;
}

[encoder setScissorRect:_clipRect];
// Clamping clip rect to the destination area
MTLScissorRect rect = _clipRect;

if (rect.x > dw) {
rect.x = dw;
}

if (rect.y > dh) {
rect.y = dh;
}

if (rect.x + rect.width > dw) {
rect.width = dw - rect.x;
}

if (rect.y + rect.height > dh) {
rect.height = dh - rect.y;
}

[encoder setScissorRect:rect];
if (_clipType == NO_CLIP || _clipType == RECT_CLIP) {
// NOTE: It seems that we can use the same encoder (with disabled stencil test) when mode changes from SHAPE to RECT.
// But [encoder setDepthStencilState:nil] causes crash, so we have to recreate encoder in such case.
@@ -194,16 +194,6 @@ + (void) _getMTLConfigInfo: (NSMutableArray *)argValue {
return;
}

NSView *scratchSurface =
[[NSView alloc]
initWithFrame: contentRect];
if (scratchSurface == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: NSView is NULL");
[argValue addObject: [NSNumber numberWithLong: 0L]];
return;
}
[window setContentView: scratchSurface];

MTLContext *mtlc = [[MTLContext alloc] initWithDevice:CGDirectDisplayCopyCurrentMetalDevice(displayID)
shadersLib:mtlShadersLib];
if (mtlc == 0L) {
@@ -52,6 +52,7 @@
static MTLRenderPipelineDescriptor * templateRenderPipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateTexturePipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateAATexturePipelineDesc = nil;
static MTLRenderPipelineDescriptor * templateLCDPipelineDesc = nil;
static void setTxtUniforms(
id<MTLRenderCommandEncoder> encoder, int color, int mode, int interpolation, bool repeat, jfloat extraAlpha,
const SurfaceRasterFlags * srcFlags, const SurfaceRasterFlags * dstFlags
@@ -93,6 +94,17 @@ static void initTemplatePipelineDescriptors() {
templateAATexturePipelineDesc = [templateTexturePipelineDesc copy];
templateAATexturePipelineDesc.label = @"template_aa_texture";

templateLCDPipelineDesc = [MTLRenderPipelineDescriptor new];
templateLCDPipelineDesc.sampleCount = 1;
templateLCDPipelineDesc.vertexDescriptor = vertDesc;
templateLCDPipelineDesc.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].format = MTLVertexFormatFloat2;
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].offset = 2*sizeof(float);
templateLCDPipelineDesc.vertexDescriptor.attributes[VertexAttributeTexPos].bufferIndex = MeshVertexBuffer;
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stride = sizeof(struct TxtVertex);
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepRate = 1;
templateLCDPipelineDesc.vertexDescriptor.layouts[MeshVertexBuffer].stepFunction = MTLVertexStepFunctionPerVertex;
templateLCDPipelineDesc.label = @"template_lcd";
}


@@ -160,6 +172,11 @@ - (void)setPipelineState:(id<MTLRenderCommandEncoder>)encoder
if (renderOptions->isText) {
fragShader = @"frag_text";
}
if (renderOptions->isLCD) {
vertShader = @"vert_txt_lcd";
fragShader = @"lcd_color";
rpDesc = [[templateLCDPipelineDesc copy] autorelease];
}
setTxtUniforms(encoder, _color, 1,
renderOptions->interpolation, NO, [mtlc.composite getExtraAlpha], &renderOptions->srcFlags,
&renderOptions->dstFlags);
@@ -84,7 +84,7 @@ - (NSPointerArray * ) getSubStates:(NSString *)vertexShaderId fragmentShader:(NS
vertexShaderId:(NSString *)vertexShaderId
fragmentShaderId:(NSString *)fragmentShaderId
{
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}, JNI_FALSE};
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}, JNI_FALSE, JNI_FALSE};
return [self getPipelineState:pipelineDescriptor
vertexShaderId:vertexShaderId
fragmentShaderId:fragmentShaderId
@@ -98,7 +98,7 @@ - (NSPointerArray * ) getSubStates:(NSString *)vertexShaderId fragmentShader:(NS
fragmentShaderId:(NSString *)fragmentShaderId
stencilNeeded:(bool)stencilNeeded
{
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}, JNI_FALSE};
RenderOptions defaultOptions = {JNI_FALSE, JNI_FALSE, 0/*unused*/, {JNI_FALSE, JNI_TRUE}, {JNI_FALSE, JNI_TRUE}, JNI_FALSE, JNI_FALSE};
return [self getPipelineState:pipelineDescriptor
vertexShaderId:vertexShaderId
fragmentShaderId:fragmentShaderId
@@ -317,7 +317,7 @@ void MTLRenderer_DrawPoly(MTLContext *mtlc, BMTLSDOps * dstOps,

const int SCANLINE_MAX_VERTEX_SIZE = 4096;
const int VERTEX_STRUCT_SIZE = 8;
const int NUM_OF_VERTICES_PER_SCANLINE = 3;
const int NUM_OF_VERTICES_PER_SCANLINE = 2;

void
MTLRenderer_DrawScanlines(MTLContext *mtlc, BMTLSDOps * dstOps,