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

render: Initial attempt at adding SDL_GenerateTextureMipmap(). #10254

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions include/SDL3/SDL_render.h
Original file line number Diff line number Diff line change
Expand Up @@ -1230,6 +1230,25 @@ extern SDL_DECLSPEC int SDLCALL SDL_LockTextureToSurface(SDL_Texture *texture,
*/
extern SDL_DECLSPEC void SDLCALL SDL_UnlockTexture(SDL_Texture *texture);

/**
* Generate mipmaps for a texture.
*
* This may make this texture render better when downscaling for future
* draw calls. When updating a texture, call this function to regenerate
* mipmaps.
*
* This might not be a cheap operation, use it sparingly!
*
* Not every renderer supports this, but when unsupported, downscaled textures
* will just perhaps scale a little less well.
*
* \param texture a texture locked by SDL_LockTexture().
* \returns 0 on success, or -1 on error.
*
* \since This function is available since SDL 3.0.0.
*/
extern SDL_DECLSPEC int SDLCALL SDL_GenerateTextureMipmap(SDL_Texture *texture);

/**
* Set a texture as the current rendering target.
*
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi.sym
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ SDL3_0.0.0 {
SDL_GamepadHasButton;
SDL_GamepadHasSensor;
SDL_GamepadSensorEnabled;
SDL_GenerateTextureMipmap;
SDL_GetAndroidActivity;
SDL_GetAndroidCachePath;
SDL_GetAndroidExternalStoragePath;
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
#define SDL_GamepadHasButton SDL_GamepadHasButton_REAL
#define SDL_GamepadHasSensor SDL_GamepadHasSensor_REAL
#define SDL_GamepadSensorEnabled SDL_GamepadSensorEnabled_REAL
#define SDL_GenerateTextureMipmap SDL_GenerateTextureMipmap_REAL
#define SDL_GetAndroidActivity SDL_GetAndroidActivity_REAL
#define SDL_GetAndroidCachePath SDL_GetAndroidCachePath_REAL
#define SDL_GetAndroidExternalStoragePath SDL_GetAndroidExternalStoragePath_REAL
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasAxis,(SDL_Gamepad *a, SDL_GamepadAxis b),
SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasButton,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadHasSensor,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return)
SDL_DYNAPI_PROC(SDL_bool,SDL_GamepadSensorEnabled,(SDL_Gamepad *a, SDL_SensorType b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GenerateTextureMipmap,(SDL_Texture *a),(a),return)
SDL_DYNAPI_PROC(void*,SDL_GetAndroidActivity,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAndroidCachePath,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetAndroidExternalStoragePath,(void),(),return)
Expand Down
15 changes: 15 additions & 0 deletions src/render/SDL_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -2410,6 +2410,21 @@ static int SDL_SetRenderTargetInternal(SDL_Renderer *renderer, SDL_Texture *text
return 0;
}

int SDL_GenerateTextureMipmap(SDL_Texture *texture)
{
CHECK_TEXTURE_MAGIC(texture, -1);

SDL_Renderer *renderer = texture->renderer;

if (!renderer->GenMipmaps) {
return SDL_Unsupported();
} else if (FlushRenderCommandsIfTextureNeeded(texture) < 0) {
return -1;
}

return renderer->GenMipmaps(renderer, texture);
}

int SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
{
if (!texture && renderer->logical_target) {
Expand Down
1 change: 1 addition & 0 deletions src/render/SDL_sysrender.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ struct SDL_Renderer
const SDL_Rect *rect, void **pixels, int *pitch);
void (*UnlockTexture)(SDL_Renderer *renderer, SDL_Texture *texture);
void (*SetTextureScaleMode)(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode);
int (*GenMipmaps)(SDL_Renderer *renderer, SDL_Texture *texture);
int (*SetRenderTarget)(SDL_Renderer *renderer, SDL_Texture *texture);
SDL_Surface *(*RenderReadPixels)(SDL_Renderer *renderer, const SDL_Rect *rect);
int (*RenderPresent)(SDL_Renderer *renderer);
Expand Down
54 changes: 39 additions & 15 deletions src/render/opengl/SDL_render_gl.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ typedef struct
PFNGLBINDFRAMEBUFFEREXTPROC glBindFramebufferEXT;
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatusEXT;

/* glGenerateMipmap support */
PFNGLGENERATEMIPMAPPROC glGenerateMipmap;

/* Shader support */
GL_ShaderContext *shaders;

Expand Down Expand Up @@ -843,6 +846,24 @@ static void GL_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
GL_UpdateTexture(renderer, texture, rect, pixels, data->pitch);
}

static int GL_GenMipmaps(SDL_Renderer *renderer, SDL_Texture *texture)
{
GL_RenderData *renderdata = (GL_RenderData *)renderer->driverdata;
SDL_assert(renderdata->glGenerateMipmap != NULL); // should have been set up at CreateRenderer time and we shouldn't be here if it isn't!

if (texture != renderdata->drawstate.texture) {
if (renderdata->GL_ARB_multitexture_supported) {
renderdata->glActiveTextureARB(GL_TEXTURE0_ARB);
}
const GL_TextureData *texturedata = (GL_TextureData *)texture->driverdata;
renderdata->glBindTexture(renderdata->textype, texturedata->texture);
renderdata->drawstate.texture = texture;
}

renderdata->glGenerateMipmap(renderdata->textype);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mipmaps need one of GL_*_MIPMAP_* modes (e.g GL_LINEAR_MIPMAP_LINEAR) for GL_TEXTURE_MIN_FILTER to have effect. See https://www.khronos.org/opengl/wiki/Common_Mistakes#Automatic_mipmap_generation

return 0; // GL does not report failure here.
Comment on lines +863 to +864
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, glGenerateMipmap does report errors (in particular, for NPOT textures):

https://registry.khronos.org/OpenGL-Refpages/es2.0/xhtml/glGenerateMipmap.xml

}

static void GL_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ScaleMode scaleMode)
{
GL_RenderData *renderdata = (GL_RenderData *)renderer->driverdata;
Expand Down Expand Up @@ -1719,23 +1740,21 @@ static int GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pro
data->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
}

int glmajorver = 1;
const char *glverstr = (const char *)data->glGetString(GL_VERSION);
if (glverstr) {
char verbuf[16];
SDL_strlcpy(verbuf, glverstr, sizeof(verbuf));
char *ptr = SDL_strchr(verbuf, '.');
if (ptr) {
*ptr = '\0';
glmajorver = SDL_atoi(verbuf);
}
}

hint = SDL_getenv("GL_ARB_texture_non_power_of_two");
if (!hint || *hint != '0') {
SDL_bool isGL2 = SDL_FALSE;
const char *verstr = (const char *)data->glGetString(GL_VERSION);
if (verstr) {
char verbuf[16];
char *ptr;
SDL_strlcpy(verbuf, verstr, sizeof(verbuf));
ptr = SDL_strchr(verbuf, '.');
if (ptr) {
*ptr = '\0';
if (SDL_atoi(verbuf) >= 2) {
isGL2 = SDL_TRUE;
}
}
}
if (isGL2 || SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) {
if ((glmajorver >= 2) || SDL_GL_ExtensionSupported("GL_ARB_texture_non_power_of_two")) {
non_power_of_two_supported = SDL_TRUE;
}
}
Expand Down Expand Up @@ -1765,6 +1784,11 @@ static int GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pro
}
}

if (glmajorver >= 3) {
data->glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)SDL_GL_GetProcAddress("glGenerateMipmap");
renderer->GenMipmaps = (data->glGenerateMipmap != NULL) ? GL_GenMipmaps : NULL;
Comment on lines +1787 to +1789
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For OpenGL < 3, glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE) can be used as a fallback.

}

/* Check for shader support */
data->shaders = GL_CreateShaderContext();
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER, "OpenGL shaders: %s",
Expand Down
1 change: 1 addition & 0 deletions src/render/opengles2/SDL_gles2funcs.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,4 @@ SDL_PROC(void, glDeleteBuffers, (GLsizei, const GLuint *))
SDL_PROC(void, glBindBuffer, (GLenum, GLuint))
SDL_PROC(void, glBufferData, (GLenum, GLsizeiptr, const GLvoid *, GLenum))
SDL_PROC(void, glBufferSubData, (GLenum, GLintptr, GLsizeiptr, const GLvoid *))
SDL_PROC(void, glGenerateMipmap, (GLenum))
17 changes: 17 additions & 0 deletions src/render/opengles2/SDL_render_gles2.c
Original file line number Diff line number Diff line change
Expand Up @@ -1906,6 +1906,22 @@ static void GLES2_SetTextureScaleMode(SDL_Renderer *renderer, SDL_Texture *textu
renderdata->glTexParameteri(data->texture_type, GL_TEXTURE_MAG_FILTER, glScaleMode);
}

static int GLES2_GenMipmaps(SDL_Renderer *renderer, SDL_Texture *texture)
{
GLES2_RenderData *renderdata = (GLES2_RenderData *)renderer->driverdata;
SDL_assert(renderdata->glGenerateMipmap != NULL); // this should have been set up at CreateRenderer time and we shouldn't be here if it isn't!

const GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
if (texture != renderdata->drawstate.texture) {
renderdata->glActiveTexture(GL_TEXTURE0);
renderdata->glBindTexture(tdata->texture_type, tdata->texture);
renderdata->drawstate.texture = texture;
}

renderdata->glGenerateMipmap(tdata->texture_type);
return 0; // GLES2 does not report failure here.
}

static int GLES2_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
{
GLES2_RenderData *data = (GLES2_RenderData *)renderer->driverdata;
Expand Down Expand Up @@ -2152,6 +2168,7 @@ static int GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_
renderer->LockTexture = GLES2_LockTexture;
renderer->UnlockTexture = GLES2_UnlockTexture;
renderer->SetTextureScaleMode = GLES2_SetTextureScaleMode;
renderer->GenMipmaps = GLES2_GenMipmaps; // always available since OpenGL ES 2.0.
renderer->SetRenderTarget = GLES2_SetRenderTarget;
renderer->QueueSetViewport = GLES2_QueueNoOp;
renderer->QueueSetDrawColor = GLES2_QueueNoOp;
Expand Down
Loading