render: Update Metal and GL backends to use new high-level features, etc.
Now nothing is uploaded as dynamic data with Metal's setVertexBytes, etc; it's all in the one big vertex buffer, now.
parent
cc56de44a4
commit
2241b33f55
|
@ -64,6 +64,8 @@ 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_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd);
|
||||
static int METAL_QueueSetDrawColor(SDL_Renderer * renderer, SDL_RenderCommand *cmd);
|
||||
static int METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points,
|
||||
int count);
|
||||
static int METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects,
|
||||
|
@ -621,6 +623,8 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
|
|||
renderer->LockTexture = METAL_LockTexture;
|
||||
renderer->UnlockTexture = METAL_UnlockTexture;
|
||||
renderer->SetRenderTarget = METAL_SetRenderTarget;
|
||||
renderer->QueueSetViewport = METAL_QueueSetViewport;
|
||||
renderer->QueueSetDrawColor = METAL_QueueSetDrawColor;
|
||||
renderer->QueueDrawPoints = METAL_QueueDrawPoints;
|
||||
renderer->QueueDrawLines = METAL_QueueDrawPoints; // lines and points queue the same way.
|
||||
renderer->QueueFillRects = METAL_QueueFillRects;
|
||||
|
@ -637,6 +641,8 @@ METAL_CreateRenderer(SDL_Window * window, Uint32 flags)
|
|||
renderer->info = METAL_RenderDriver.info;
|
||||
renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
|
||||
|
||||
renderer->always_batch = SDL_TRUE;
|
||||
|
||||
#if defined(__MACOSX__) && defined(MAC_OS_X_VERSION_10_13)
|
||||
if (@available(macOS 10.13, *)) {
|
||||
data.mtllayer.displaySyncEnabled = (flags & SDL_RENDERER_PRESENTVSYNC) != 0;
|
||||
|
@ -740,6 +746,11 @@ METAL_ActivateRenderCommandEncoder(SDL_Renderer * renderer, MTLLoadAction load,
|
|||
}
|
||||
|
||||
data.activepipelines = ChooseShaderPipelines(data, mtltexture.pixelFormat);
|
||||
|
||||
// make sure this has a definite place in the queue. This way it will
|
||||
// execute reliably whether the app tries to make its own command buffers
|
||||
// or whatever. This means we can _always_ batch rendering commands!
|
||||
[data.mtlcmdbuffer enqueue];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1026,11 +1037,51 @@ normtex(const float _val, const float len)
|
|||
return _val / len;
|
||||
}
|
||||
|
||||
static int
|
||||
METAL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
|
||||
{
|
||||
float projection[4][4]; /* Prepare an orthographic projection */
|
||||
const int w = cmd->data.viewport.rect.w;
|
||||
const int h = cmd->data.viewport.rect.h;
|
||||
const size_t matrixlen = sizeof (projection);
|
||||
float *matrix = (float *) SDL_AllocateRenderVertices(renderer, matrixlen, CONSTANT_ALIGN, &cmd->data.viewport.first);
|
||||
if (!matrix) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SDL_memset(projection, '\0', matrixlen);
|
||||
if (w && h) {
|
||||
projection[0][0] = 2.0f / w;
|
||||
projection[1][1] = -2.0f / h;
|
||||
projection[3][0] = -1.0f;
|
||||
projection[3][1] = 1.0f;
|
||||
projection[3][3] = 1.0f;
|
||||
}
|
||||
SDL_memcpy(matrix, projection, matrixlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
METAL_QueueSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
|
||||
{
|
||||
const size_t vertlen = sizeof (float) * 4;
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, CONSTANT_ALIGN, &cmd->data.color.first);
|
||||
if (!verts) {
|
||||
return -1;
|
||||
}
|
||||
*(verts++) = ((float)cmd->data.color.r) / 255.0f;
|
||||
*(verts++) = ((float)cmd->data.color.g) / 255.0f;
|
||||
*(verts++) = ((float)cmd->data.color.b) / 255.0f;
|
||||
*(verts++) = ((float)cmd->data.color.a) / 255.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
METAL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
|
||||
{
|
||||
const size_t vertlen = (sizeof (float) * 2) * count;
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first);
|
||||
if (!verts) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1044,7 +1095,7 @@ METAL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_
|
|||
{
|
||||
// !!! FIXME: use an index buffer
|
||||
const size_t vertlen = (sizeof (float) * 8) * count;
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first);
|
||||
if (!verts) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1082,7 +1133,7 @@ METAL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * t
|
|||
const float texh = (float) texturedata.mtltexture.height;
|
||||
// !!! FIXME: use an index buffer
|
||||
const size_t vertlen = (sizeof (float) * 16);
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first);
|
||||
if (!verts) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1123,7 +1174,7 @@ METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture *
|
|||
float minu, maxu, minv, maxv;
|
||||
// !!! FIXME: use an index buffer
|
||||
const size_t vertlen = (sizeof (float) * 32);
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, &cmd->data.draw.first);
|
||||
float *verts = (float *) SDL_AllocateRenderVertices(renderer, vertlen, 0, &cmd->data.draw.first);
|
||||
if (!verts) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -1185,16 +1236,21 @@ METAL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture *
|
|||
|
||||
typedef struct
|
||||
{
|
||||
#if __has_feature(objc_arc)
|
||||
__unsafe_unretained id<MTLRenderPipelineState> pipeline;
|
||||
#else
|
||||
id<MTLRenderPipelineState> pipeline;
|
||||
#endif
|
||||
size_t constants_offset;
|
||||
SDL_Texture *texture;
|
||||
Uint32 color;
|
||||
SDL_bool color_dirty;
|
||||
SDL_bool cliprect_dirty;
|
||||
SDL_bool cliprect_enabled;
|
||||
SDL_Rect cliprect;
|
||||
SDL_bool viewport_dirty;
|
||||
SDL_Rect viewport;
|
||||
SDL_Rect cliprect;
|
||||
size_t projection_offset;
|
||||
SDL_bool color_dirty;
|
||||
size_t color_offset;
|
||||
} METAL_DrawStateCache;
|
||||
|
||||
static void
|
||||
|
@ -1202,12 +1258,8 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met
|
|||
const size_t constants_offset, id<MTLBuffer> mtlbufvertex, METAL_DrawStateCache *statecache)
|
||||
{
|
||||
METAL_RenderData *data = (__bridge METAL_RenderData *) renderer->driverdata;
|
||||
const Uint8 r = cmd->data.draw.r;
|
||||
const Uint8 g = cmd->data.draw.g;
|
||||
const Uint8 b = cmd->data.draw.b;
|
||||
const Uint8 a = cmd->data.draw.a;
|
||||
const Uint32 color = ((a << 24) | (r << 16) | (g << 8) | b);
|
||||
const SDL_BlendMode blend = cmd->data.draw.blend;
|
||||
size_t first = cmd->data.draw.first;
|
||||
id<MTLRenderPipelineState> newpipeline;
|
||||
|
||||
METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionLoad, NULL);
|
||||
|
@ -1221,20 +1273,7 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met
|
|||
viewport.znear = 0.0;
|
||||
viewport.zfar = 1.0;
|
||||
[data.mtlcmdencoder setViewport:viewport];
|
||||
|
||||
float projection[4][4]; /* Prepare an orthographic projection */
|
||||
SDL_memset(projection, '\0', sizeof (projection));
|
||||
|
||||
if (statecache->viewport.w && statecache->viewport.h) {
|
||||
projection[0][0] = 2.0f / statecache->viewport.w;
|
||||
projection[1][1] = -2.0f / statecache->viewport.h;
|
||||
projection[3][0] = -1.0f;
|
||||
projection[3][1] = 1.0f;
|
||||
projection[3][3] = 1.0f;
|
||||
}
|
||||
|
||||
// !!! FIXME: This should be in a buffer...
|
||||
[data.mtlcmdencoder setVertexBytes:projection length:sizeof(float)*16 atIndex:2];
|
||||
[data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:statecache->projection_offset atIndex:2]; // projection
|
||||
statecache->viewport_dirty = SDL_FALSE;
|
||||
}
|
||||
|
||||
|
@ -1258,11 +1297,9 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met
|
|||
statecache->cliprect_dirty = SDL_FALSE;
|
||||
}
|
||||
|
||||
if (statecache->color_dirty || (color != statecache->color)) {
|
||||
const float colorf[4] = { ((float)r) / 255.0f, ((float)g) / 255.0f, ((float)b) / 255.0f, ((float)a) / 255.0f };
|
||||
[data.mtlcmdencoder setFragmentBytes:colorf length:sizeof(colorf) atIndex:0];
|
||||
if (statecache->color_dirty) {
|
||||
[data.mtlcmdencoder setFragmentBuffer:mtlbufvertex offset:statecache->color_offset atIndex:0];
|
||||
statecache->color_dirty = SDL_FALSE;
|
||||
statecache->color = color;
|
||||
}
|
||||
|
||||
newpipeline = ChoosePipelineState(data, data.activepipelines, shader, blend);
|
||||
|
@ -1278,7 +1315,7 @@ SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Met
|
|||
statecache->constants_offset = constants_offset;
|
||||
}
|
||||
|
||||
[data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:cmd->data.draw.first atIndex:0]; // position
|
||||
[data.mtlcmdencoder setVertexBuffer:mtlbufvertex offset:first atIndex:0]; // position
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1320,13 +1357,11 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
|
|||
statecache.pipeline = nil;
|
||||
statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
|
||||
statecache.texture = NULL;
|
||||
statecache.color = ((renderer->a << 24) | (renderer->r << 16) | (renderer->g << 8) | renderer->b);
|
||||
statecache.color_dirty = SDL_TRUE;
|
||||
statecache.cliprect_dirty = SDL_TRUE;
|
||||
statecache.viewport_dirty = SDL_TRUE; // TRUE so we set ortho matrix
|
||||
statecache.cliprect_enabled = renderer->clipping_enabled;
|
||||
SDL_memcpy(&statecache.viewport, &renderer->viewport, sizeof (statecache.viewport));
|
||||
SDL_memcpy(&statecache.cliprect, &renderer->clip_rect, sizeof (statecache.cliprect));
|
||||
statecache.viewport_dirty = SDL_TRUE;
|
||||
statecache.projection_offset = 0;
|
||||
statecache.color_offset = 0;
|
||||
|
||||
// !!! FIXME: have a ring of pre-made MTLBuffers we cycle through? How expensive is creation?
|
||||
id<MTLBuffer> mtlbufvertexstaging = [data.mtldevice newBufferWithLength:vertsize options:MTLResourceStorageModeShared];
|
||||
|
@ -1357,20 +1392,22 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
|
|||
while (cmd) {
|
||||
switch (cmd->command) {
|
||||
case SDL_RENDERCMD_SETVIEWPORT: {
|
||||
if (SDL_memcmp(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport)) != 0) {
|
||||
SDL_memcpy(&statecache.viewport, &cmd->data.viewport, sizeof (statecache.viewport));
|
||||
statecache.viewport_dirty = SDL_TRUE;
|
||||
}
|
||||
SDL_memcpy(&statecache.viewport, &cmd->data.viewport.rect, sizeof (statecache.viewport));
|
||||
statecache.projection_offset = cmd->data.viewport.first;
|
||||
statecache.viewport_dirty = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_RENDERCMD_SETCLIPRECT: {
|
||||
if ((statecache.cliprect_enabled != cmd->data.cliprect.enabled) ||
|
||||
(SDL_memcmp(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect)) != 0)) {
|
||||
SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect));
|
||||
statecache.cliprect_enabled = cmd->data.cliprect.enabled;
|
||||
statecache.cliprect_dirty = SDL_TRUE;
|
||||
}
|
||||
SDL_memcpy(&statecache.cliprect, &cmd->data.cliprect.rect, sizeof (statecache.cliprect));
|
||||
statecache.cliprect_enabled = cmd->data.cliprect.enabled;
|
||||
statecache.cliprect_dirty = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_RENDERCMD_SETDRAWCOLOR: {
|
||||
statecache.color_offset = cmd->data.color.first;
|
||||
statecache.color_dirty = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1380,16 +1417,13 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
|
|||
operation via MTLLoadActionClear. */
|
||||
if (data.mtlcmdencoder != nil) {
|
||||
[data.mtlcmdencoder endEncoding];
|
||||
|
||||
// !!! FIXME: have to commit, or an uncommitted but enqueued buffer will prevent the frame from finishing.
|
||||
[data.mtlcmdbuffer commit];
|
||||
data.mtlcmdencoder = nil;
|
||||
data.mtlcmdbuffer = nil;
|
||||
}
|
||||
|
||||
const Uint8 r = cmd->data.color.r;
|
||||
const Uint8 g = cmd->data.color.g;
|
||||
const Uint8 b = cmd->data.color.b;
|
||||
const Uint8 a = cmd->data.color.a;
|
||||
MTLClearColor color = MTLClearColorMake(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
|
||||
|
||||
// force all this state to be reconfigured on next command buffer.
|
||||
statecache.pipeline = nil;
|
||||
statecache.constants_offset = CONSTANTS_OFFSET_INVALID;
|
||||
|
@ -1398,6 +1432,12 @@ METAL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *ver
|
|||
statecache.cliprect_dirty = SDL_TRUE;
|
||||
statecache.viewport_dirty = SDL_TRUE;
|
||||
|
||||
const Uint8 r = cmd->data.color.r;
|
||||
const Uint8 g = cmd->data.color.g;
|
||||
const Uint8 b = cmd->data.color.b;
|
||||
const Uint8 a = cmd->data.color.a;
|
||||
MTLClearColor color = MTLClearColorMake(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
|
||||
|
||||
// get new command encoder, set up with an initial clear operation.
|
||||
METAL_ActivateRenderCommandEncoder(renderer, MTLLoadActionClear, &color);
|
||||
break;
|
||||
|
|
|
@ -68,6 +68,7 @@ static int GL_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture,
|
|||
const SDL_Rect * rect, void **pixels, int *pitch);
|
||||
static void GL_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture);
|
||||
static int GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture);
|
||||
static int GL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd);
|
||||
static int GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd,
|
||||
const SDL_FPoint * points, int count);
|
||||
static int GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd,
|
||||
|
@ -391,6 +392,8 @@ GL_CreateRenderer(SDL_Window * window, Uint32 flags)
|
|||
renderer->LockTexture = GL_LockTexture;
|
||||
renderer->UnlockTexture = GL_UnlockTexture;
|
||||
renderer->SetRenderTarget = GL_SetRenderTarget;
|
||||
renderer->QueueSetViewport = GL_QueueSetViewport;
|
||||
renderer->QueueSetDrawColor = GL_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */
|
||||
renderer->QueueDrawPoints = GL_QueueDrawPoints;
|
||||
renderer->QueueDrawLines = GL_QueueDrawPoints; /* lines and points queue vertices the same way. */
|
||||
renderer->QueueFillRects = GL_QueueFillRects;
|
||||
|
@ -1009,10 +1012,16 @@ GL_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture)
|
|||
/* !!! FIXME: all these Queue* calls set up the vertex buffer the way the immediate mode
|
||||
!!! FIXME: renderer wants it, but this might want to operate differently if we move to
|
||||
!!! FIXME: VBOs at some point. */
|
||||
static int
|
||||
GL_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd)
|
||||
{
|
||||
return 0; /* nothing to do in this backend. */
|
||||
}
|
||||
|
||||
static int
|
||||
GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count)
|
||||
{
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), &cmd->data.draw.first);
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (GLfloat), 0, &cmd->data.draw.first);
|
||||
size_t i;
|
||||
|
||||
if (!verts) {
|
||||
|
@ -1031,7 +1040,7 @@ GL_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FP
|
|||
static int
|
||||
GL_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count)
|
||||
{
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), &cmd->data.draw.first);
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 4 * sizeof (GLfloat), 0, &cmd->data.draw.first);
|
||||
size_t i;
|
||||
|
||||
if (!verts) {
|
||||
|
@ -1057,7 +1066,7 @@ GL_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * text
|
|||
GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
|
||||
GLfloat minx, miny, maxx, maxy;
|
||||
GLfloat minu, maxu, minv, maxv;
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 8 * sizeof (GLfloat), &cmd->data.draw.first);
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 8 * sizeof (GLfloat), 0, &cmd->data.draw.first);
|
||||
|
||||
if (!verts) {
|
||||
return -1;
|
||||
|
@ -1100,7 +1109,7 @@ GL_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * te
|
|||
GLfloat minx, miny, maxx, maxy;
|
||||
GLfloat centerx, centery;
|
||||
GLfloat minu, maxu, minv, maxv;
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 11 * sizeof (GLfloat), &cmd->data.draw.first);
|
||||
GLfloat *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, 11 * sizeof (GLfloat), 0, &cmd->data.draw.first);
|
||||
|
||||
if (!verts) {
|
||||
return -1;
|
||||
|
@ -1338,9 +1347,14 @@ GL_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic
|
|||
|
||||
while (cmd) {
|
||||
switch (cmd->command) {
|
||||
case SDL_RENDERCMD_SETDRAWCOLOR: {
|
||||
// !!! FIXME: use this.
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_RENDERCMD_SETVIEWPORT: {
|
||||
if (SDL_memcmp(&cmd->data.viewport, &viewport, sizeof (SDL_Rect)) != 0) {
|
||||
SDL_memcpy(&viewport, &cmd->data.viewport, sizeof (SDL_Rect));
|
||||
if (SDL_memcmp(&cmd->data.viewport.rect, &viewport, sizeof (viewport)) != 0) {
|
||||
SDL_memcpy(&viewport, &cmd->data.viewport.rect, sizeof (viewport));
|
||||
data->glMatrixMode(GL_PROJECTION);
|
||||
data->glLoadIdentity();
|
||||
data->glViewport(viewport.x,
|
||||
|
|
Loading…
Reference in New Issue