SDL2源代码分析3:渲染器(SDL_Renderer)
SDL_Renderer
SDL_Renderer结构体定义了一个SDL2中的渲染器,在SDL_render.h如下所示。
/**
* A structure representing rendering state
*/
struct SDL_Renderer;
typedef struct SDL_Renderer SDL_Renderer;
在源代码工程中可以看到SDL_Renderer的定义,位于render\SDL_sysrender.h文件中。它的定义如下。
/* Define the SDL renderer structure */
struct SDL_Renderer
{
const void *magic;
void (*WindowEvent) (SDL_Renderer * renderer, const SDL_WindowEvent *event);
int (*GetOutputSize) (SDL_Renderer * renderer, int *w, int *h);
SDL_bool (*SupportsBlendMode)(SDL_Renderer * renderer, SDL_BlendMode blendMode);
int (*CreateTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
int (*QueueSetViewport) (SDL_Renderer * renderer, SDL_RenderCommand *cmd);
int (*QueueSetDrawColor) (SDL_Renderer * renderer, SDL_RenderCommand *cmd);
int (*QueueDrawPoints) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points,
int count);
int (*QueueDrawLines) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points,
int count);
int (*QueueFillRects) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects,
int count);
int (*QueueCopy) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
const SDL_Rect * srcrect, const SDL_FRect * dstrect);
int (*QueueCopyEx) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
const SDL_Rect * srcquad, const SDL_FRect * dstrect,
const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
int (*RunCommandQueue) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize);
int (*UpdateTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect, const void *pixels,
int pitch);
#if SDL_HAVE_YUV
int (*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);
int (*UpdateTextureNV) (SDL_Renderer * renderer, SDL_Texture * texture,
const SDL_Rect * rect,
const Uint8 *Yplane, int Ypitch,
const Uint8 *UVplane, int UVpitch);
#endif
int (*LockTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
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 (*SetRenderTarget) (SDL_Renderer * renderer, SDL_Texture * texture);
int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect,
Uint32 format, void * pixels, int pitch);
void (*RenderPresent) (SDL_Renderer * renderer);
void (*DestroyTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
void (*DestroyRenderer) (SDL_Renderer * renderer);
int (*GL_BindTexture) (SDL_Renderer * renderer, SDL_Texture *texture, float *texw, float *texh);
int (*GL_UnbindTexture) (SDL_Renderer * renderer, SDL_Texture *texture);
void *(*GetMetalLayer) (SDL_Renderer * renderer);
void *(*GetMetalCommandEncoder) (SDL_Renderer * renderer);
/* The current renderer info */
SDL_RendererInfo info;
/* The window associated with the renderer */
SDL_Window *window;
SDL_bool hidden;
/* The logical resolution for rendering */
int logical_w;
int logical_h;
int logical_w_backup;
int logical_h_backup;
/* Whether or not to force the viewport to even integer intervals */
SDL_bool integer_scale;
/* The drawable area within the window */
SDL_Rect viewport;
SDL_Rect viewport_backup;
/* The clip rectangle within the window */
SDL_Rect clip_rect;
SDL_Rect clip_rect_backup;
/* Wether or not the clipping rectangle is used. */
SDL_bool clipping_enabled;
SDL_bool clipping_enabled_backup;
/* The render output coordinate scale */
SDL_FPoint scale;
SDL_FPoint scale_backup;
/* The pixel to point coordinate scale */
SDL_FPoint dpi_scale;
/* Whether or not to scale relative mouse motion */
SDL_bool relative_scaling;
/* Remainder from scaled relative motion */
float xrel;
float yrel;
/* The list of textures */
SDL_Texture *textures;
SDL_Texture *target;
SDL_mutex *target_mutex;
Uint8 r, g, b, a; /**< Color for drawing operations values */
SDL_BlendMode blendMode; /**< The drawing blend mode */
SDL_bool always_batch;
SDL_bool batching;
SDL_RenderCommand *render_commands;
SDL_RenderCommand *render_commands_tail;
SDL_RenderCommand *render_commands_pool;
Uint32 render_command_generation;
Uint32 last_queued_color;
SDL_Rect last_queued_viewport;
SDL_Rect last_queued_cliprect;
SDL_bool last_queued_cliprect_enabled;
SDL_bool color_queued;
SDL_bool viewport_queued;
SDL_bool cliprect_queued;
void *vertex_data;
size_t vertex_data_used;
size_t vertex_data_allocation;
void *driverdata;
};
通过代码可以看出其中包含了一个“渲染器”应该包含的各种属性。
SDL_CreateRenderer()
函数简介
SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window * window,
int index, Uint32 flags);
参数含义如下。
window : 渲染的目标窗口。
index :打算初始化的渲染设备的索引。设置“-1”则初始化默认的渲染设备。
flags :支持以下值(位于SDL_RendererFlags定义中)
SDL_RENDERER_SOFTWARE :使用软件渲染
SDL_RENDERER_ACCELERATED :使用硬件加速
SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步
SDL_RENDERER_TARGETTEXTURE :不太懂
返回创建完成的渲染器的ID。如果创建失败则返回NULL。
源码分析
SDL_CreateRenderer()的源代码位于render\SDL_render.c中,如下所示。
SDL_Renderer *
SDL_CreateRenderer(SDL_Window * window, int index, Uint32 flags)
{
#if !SDL_RENDER_DISABLED
SDL_Renderer *renderer = NULL;
int n = SDL_GetNumRenderDrivers();
SDL_bool batching = SDL_TRUE;
const char *hint;
#if defined(__ANDROID__)
Android_ActivityMutex_Lock_Running();
#endif
if (!window) {
SDL_SetError("Invalid window");
goto error;
}
if (SDL_GetRenderer(window)) {
SDL_SetError("Renderer already associated with window");
goto error;
}
if (SDL_GetHint(SDL_HINT_RENDER_VSYNC)) {
if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
flags |= SDL_RENDERER_PRESENTVSYNC;
} else {
flags &= ~SDL_RENDERER_PRESENTVSYNC;
}
}
if (index < 0) {
hint = SDL_GetHint(SDL_HINT_RENDER_DRIVER);
if (hint) {
for (index = 0; index < n; ++index) {
const SDL_RenderDriver *driver = render_drivers[index];
if (SDL_strcasecmp(hint, driver->info.name) == 0) {
/* Create a new renderer instance */
renderer = driver->CreateRenderer(window, flags);
if (renderer) {
batching = SDL_FALSE;
}
break;
}
}
}
if (!renderer) {
for (index = 0; index < n; ++index) {
const SDL_RenderDriver *driver = render_drivers[index];
if ((driver->info.flags & flags) == flags) {
/* Create a new renderer instance */
renderer = driver->CreateRenderer(window, flags);
if (renderer) {
/* Yay, we got one! */
break;
}
}
}
}
if (index == n) {
SDL_SetError("Couldn't find matching render driver");
goto error;
}
} else {
if (index >= SDL_GetNumRenderDrivers()) {
SDL_SetError("index must be -1 or in the range of 0 - %d",
SDL_GetNumRenderDrivers() - 1);
goto error;
}
/* Create a new renderer instance */
renderer = render_drivers[index]->CreateRenderer(window, flags);
batching = SDL_FALSE;
}
if (!renderer) {
goto error;
}
VerifyDrawQueueFunctions(renderer);
/* let app/user override batching decisions. */
if (renderer->always_batch) {
batching = SDL_TRUE;
} else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) {
batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE);
}
renderer->batching = batching;
renderer->magic = &renderer_magic;
renderer->window = window;
renderer->target_mutex = SDL_CreateMutex();
renderer->scale.x = 1.0f;
renderer->scale.y = 1.0f;
renderer->dpi_scale.x = 1.0f;
renderer->dpi_scale.y = 1.0f;
/* new textures start at zero, so we start at 1 so first render doesn't flush by accident. */
renderer->render_command_generation = 1;
if (window && renderer->GetOutputSize) {
int window_w, window_h;
int output_w, output_h;
if (renderer->GetOutputSize(renderer, &output_w, &output_h) == 0) {
SDL_GetWindowSize(renderer->window, &window_w, &window_h);
renderer->dpi_scale.x = (float)window_w / output_w;
renderer->dpi_scale.y = (float)window_h / output_h;
}
}
renderer->relative_scaling = SDL_GetHintBoolean(SDL_HINT_MOUSE_RELATIVE_SCALING, SDL_TRUE);
if (SDL_GetWindowFlags(window) & (SDL_WINDOW_HIDDEN|SDL_WINDOW_MINIMIZED)) {
renderer->hidden = SDL_TRUE;
} else {
renderer->hidden = SDL_FALSE;
}
SDL_SetWindowData(window, SDL_WINDOWRENDERDATA, renderer);
SDL_RenderSetViewport(renderer, NULL);
SDL_AddEventWatch(SDL_RendererEventWatch, renderer);
SDL_LogInfo(SDL_LOG_CATEGORY_RENDER,
"Created renderer: %s", renderer->info.name);
#if defined(__ANDROID__)
Android_ActivityMutex_Unlock();
#endif
return renderer;
error:
#if defined(__ANDROID__)
Android_ActivityMutex_Unlock();
#endif
return NULL;
#else
SDL_SetError("SDL not built with rendering support");
return NULL;
#endif
}
SDL_CreateRenderer()中最重要的一个函数就是它调用了SDL_RenderDriver的CreateRenderer()方法。通过该方法可以创建一个渲染器。围绕着这个方法,包含了一些初始化工作以及一些收尾工作。下面针对这个最核心的函数进行分析。
我们首先来看一下SDL_RenderDriver这个结构体。从字面的意思可以看出它代表了“渲染器的驱动程序”。这个结构体的定义如下。
/* Define the SDL render driver structure */
struct SDL_RenderDriver
{
SDL_Renderer *(*CreateRenderer) (SDL_Window * window, Uint32 flags);
/* Info about the renderer capabilities */
SDL_RendererInfo info;
};
从代码中可以看出,这个结构体的成员比较简单,包含了一个函数指针CreateRenderer()和一个存储信息的SDL_RendererInfo类型的结构体info。CreateRenderer()是用于创建渲染器的函数,而SDL_RendererInfo则包含了该结构体的一些信息,可以看一下SDL_RendererInfo的定义。
/**
* Information on the capabilities of a render driver or context.
*/
typedef struct SDL_RendererInfo
{
const char *name; /**< The name of the renderer */
Uint32 flags; /**< Supported ::SDL_RendererFlags */
Uint32 num_texture_formats; /**< The number of available texture formats */
Uint32 texture_formats[16]; /**< The available texture formats */
int max_texture_width; /**< The maximum texture width */
int max_texture_height; /**< The maximum texture height */
} SDL_RendererInfo;
在SDL中有一个全局的SDL_RenderDriver类型的静态数组render_drivers,其中存储了SDL支持的所有渲染器。该数组定义如下。
static const SDL_RenderDriver *render_drivers[] = {
#if SDL_VIDEO_RENDER_D3D
&D3D_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_D3D11
&D3D11_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_METAL
&METAL_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_OGL
&GL_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_OGL_ES2
&GLES2_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_OGL_ES
&GLES_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_DIRECTFB
&DirectFB_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_PSP
&PSP_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_VITA_GXM
&VITA_GXM_RenderDriver,
#endif
#if SDL_VIDEO_RENDER_SW
&SW_RenderDriver
#endif
};
从render_drivers数组的定义可以看出,其中包含了Direct3D,OpenGL,OpenGL ES等各种渲染器的驱动程序。我们可以选择几个看一下。
例如Direct3D的渲染器驱动程序D3D_RenderDriver的定义如下(位于render\direct3d\SDL_render_d3d.c)。
SDL_RenderDriver D3D_RenderDriver = {
D3D_CreateRenderer,
{
"direct3d",
(SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE),
1,
{SDL_PIXELFORMAT_ARGB8888},
0,
0}
};
可以看出创建OpenGL渲染器的函数是GL_CreateRenderer()。
参考文献
1、https://blog.csdn.net/leixiaohua1020/article/details/40723085