361 lines
12 KiB
C
361 lines
12 KiB
C
/*
|
|
*
|
|
* Singe 3
|
|
* Copyright (C) 2006-2026 Scott Duensing <scott@kangaroopunch.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 3
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
// Backend selection and dispatch.
|
|
//
|
|
// One indirect call per entry point. That is nothing beside the work each one goes on to do, and it
|
|
// buys runtime selection: the same binary runs on a machine with Vulkan and on one with GLES only.
|
|
|
|
#include "render.h"
|
|
#include "util.h"
|
|
|
|
|
|
static const RenderBackendT *_backend = NULL;
|
|
static SDL_Renderer *_renderer = NULL;
|
|
static RenderApiT _api = RENDER_NONE;
|
|
|
|
|
|
RenderApiT renderApi(void) {
|
|
return _api;
|
|
}
|
|
|
|
|
|
const char *renderApiName(void) {
|
|
switch (_api) {
|
|
case RENDER_GPU: return "SDL_GPU";
|
|
case RENDER_GLES: return "OpenGL ES";
|
|
default: return "none";
|
|
}
|
|
}
|
|
|
|
|
|
// GLES shares one context with SDL_Renderer, which caches its own drawing state. SDL_FlushRenderer
|
|
// both flushes the queue and invalidates that cache (SDL_render.c), which is what makes handing the
|
|
// context over safe rather than merely lucky -- see PLAN.md section 56, phase 0. Under SDL_GPU
|
|
// there is no shared state and this costs a comparison.
|
|
void renderBegin(void) {
|
|
if ((_api == RENDER_GLES) && (_renderer != NULL)) {
|
|
SDL_FlushRenderer(_renderer);
|
|
}
|
|
}
|
|
|
|
|
|
// Nothing to undo under SDL_GPU; it owns no state SDL_Renderer can see. GLES shares one context,
|
|
// so it puts back the bindings SDL does not track for itself.
|
|
void renderEnd(void) {
|
|
if (_api == RENDER_GLES) {
|
|
renderGlesRestore();
|
|
}
|
|
}
|
|
|
|
|
|
void renderSelect(RenderApiT api, const RenderBackendT *backend, SDL_Renderer *renderer) {
|
|
_api = backend != NULL ? api : RENDER_NONE;
|
|
_backend = backend;
|
|
_renderer = renderer;
|
|
utilTrace("Render: %s", renderApiName());
|
|
}
|
|
|
|
|
|
SDL_GPUTexture *renderTextureFor(SDL_Texture *texture) {
|
|
if (texture == NULL) {
|
|
return NULL;
|
|
}
|
|
if (_api == RENDER_GLES) {
|
|
float width = 0.0f;
|
|
float height = 0.0f;
|
|
|
|
SDL_GetTextureSize(texture, &width, &height);
|
|
return renderGlesTextureWrap((Uint32)SDL_GetNumberProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_NUMBER, 0), (int32_t)width, (int32_t)height);
|
|
}
|
|
return (SDL_GPUTexture *)SDL_GetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, NULL);
|
|
}
|
|
|
|
|
|
SDL_Texture *renderWrapTexture(SDL_Renderer *renderer, SDL_GPUTexture *texture, SDL_PixelFormat format, int32_t width, int32_t height) {
|
|
SDL_PropertiesID props;
|
|
SDL_Texture *result;
|
|
|
|
if ((renderer == NULL) || (texture == NULL)) {
|
|
return NULL;
|
|
}
|
|
props = SDL_CreateProperties();
|
|
if (_api == RENDER_GLES) {
|
|
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_NUMBER, (Sint64)renderGlesTextureName(texture));
|
|
// Core GLES has no BGRA storage, so a caller asking for BGRA32 -- which the scene does, as
|
|
// its way of naming the swapchain's layout -- really has an RGBA8 texture. Saying otherwise
|
|
// leaves SDL drawing nothing at all, which is how this was found.
|
|
if ((format == SDL_PIXELFORMAT_BGRA32) || (format == SDL_PIXELFORMAT_BGRX32)) {
|
|
format = SDL_PIXELFORMAT_RGBA32;
|
|
}
|
|
} else {
|
|
SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_CREATE_GPU_TEXTURE_POINTER, texture);
|
|
}
|
|
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format);
|
|
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC);
|
|
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, width);
|
|
SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, height);
|
|
result = SDL_CreateTextureWithProperties(renderer, props);
|
|
SDL_DestroyProperties(props);
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// Every run of rgpu* drawing sits between acquiring a command buffer and submitting it, so that is
|
|
// where the context is taken and given back. Bracketing here rather than at the callers means
|
|
// scene.c and guiRender.cpp need to know nothing about it.
|
|
SDL_GPUCommandBuffer *rgpuAcquireCommandBuffer(SDL_GPUDevice *device) {
|
|
renderBegin();
|
|
return _backend->acquireCommandBuffer(device);
|
|
}
|
|
|
|
|
|
SDL_GPUCopyPass *rgpuBeginCopyPass(SDL_GPUCommandBuffer *commandBuffer) {
|
|
return _backend->beginCopyPass(commandBuffer);
|
|
}
|
|
|
|
|
|
SDL_GPURenderPass *rgpuBeginRenderPass(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUColorTargetInfo *colorTargetInfos, Uint32 numColorTargets, const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo) {
|
|
return _backend->beginRenderPass(commandBuffer, colorTargetInfos, numColorTargets, depthStencilTargetInfo);
|
|
}
|
|
|
|
|
|
void rgpuBindFragmentSamplers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUTextureSamplerBinding *textureSamplerBindings, Uint32 numBindings) {
|
|
_backend->bindFragmentSamplers(renderPass, firstSlot, textureSamplerBindings, numBindings);
|
|
}
|
|
|
|
|
|
void rgpuBindGraphicsPipeline(SDL_GPURenderPass *renderPass, SDL_GPUGraphicsPipeline *graphicsPipeline) {
|
|
_backend->bindGraphicsPipeline(renderPass, graphicsPipeline);
|
|
}
|
|
|
|
|
|
void rgpuBindIndexBuffer(SDL_GPURenderPass *renderPass, const SDL_GPUBufferBinding *binding, SDL_GPUIndexElementSize indexElementSize) {
|
|
_backend->bindIndexBuffer(renderPass, binding, indexElementSize);
|
|
}
|
|
|
|
|
|
void rgpuBindVertexBuffers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, const SDL_GPUBufferBinding *bindings, Uint32 numBindings) {
|
|
_backend->bindVertexBuffers(renderPass, firstSlot, bindings, numBindings);
|
|
}
|
|
|
|
|
|
void rgpuBindVertexStorageBuffers(SDL_GPURenderPass *renderPass, Uint32 firstSlot, SDL_GPUBuffer *const *storageBuffers, Uint32 numBindings) {
|
|
_backend->bindVertexStorageBuffers(renderPass, firstSlot, storageBuffers, numBindings);
|
|
}
|
|
|
|
|
|
void rgpuBlitTexture(SDL_GPUCommandBuffer *commandBuffer, const SDL_GPUBlitInfo *info) {
|
|
_backend->blitTexture(commandBuffer, info);
|
|
}
|
|
|
|
|
|
bool rgpuCancelCommandBuffer(SDL_GPUCommandBuffer *commandBuffer) {
|
|
bool ok = _backend->cancelCommandBuffer(commandBuffer);
|
|
|
|
renderEnd();
|
|
return ok;
|
|
}
|
|
|
|
|
|
void rgpuCopyTextureToTexture(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureLocation *source, const SDL_GPUTextureLocation *destination, Uint32 w, Uint32 h, Uint32 d, bool cycle) {
|
|
_backend->copyTextureToTexture(copyPass, source, destination, w, h, d, cycle);
|
|
}
|
|
|
|
|
|
SDL_GPUBuffer *rgpuCreateBuffer(SDL_GPUDevice *device, const SDL_GPUBufferCreateInfo *createinfo) {
|
|
return _backend->createBuffer(device, createinfo);
|
|
}
|
|
|
|
|
|
SDL_GPUDevice *rgpuCreateDevice(SDL_GPUShaderFormat formatFlags, bool debugMode, const char *name) {
|
|
return _backend->createDevice(formatFlags, debugMode, name);
|
|
}
|
|
|
|
|
|
SDL_GPUGraphicsPipeline *rgpuCreateGraphicsPipeline(SDL_GPUDevice *device, const SDL_GPUGraphicsPipelineCreateInfo *createinfo) {
|
|
return _backend->createGraphicsPipeline(device, createinfo);
|
|
}
|
|
|
|
|
|
SDL_GPUSampler *rgpuCreateSampler(SDL_GPUDevice *device, const SDL_GPUSamplerCreateInfo *createinfo) {
|
|
return _backend->createSampler(device, createinfo);
|
|
}
|
|
|
|
|
|
SDL_GPUShader *rgpuCreateShader(SDL_GPUDevice *device, const SDL_GPUShaderCreateInfo *createinfo) {
|
|
return _backend->createShader(device, createinfo);
|
|
}
|
|
|
|
|
|
SDL_GPUTexture *rgpuCreateTexture(SDL_GPUDevice *device, const SDL_GPUTextureCreateInfo *createinfo) {
|
|
return _backend->createTexture(device, createinfo);
|
|
}
|
|
|
|
|
|
SDL_GPUTransferBuffer *rgpuCreateTransferBuffer(SDL_GPUDevice *device, const SDL_GPUTransferBufferCreateInfo *createinfo) {
|
|
return _backend->createTransferBuffer(device, createinfo);
|
|
}
|
|
|
|
|
|
void rgpuDestroyDevice(SDL_GPUDevice *device) {
|
|
_backend->destroyDevice(device);
|
|
}
|
|
|
|
|
|
void rgpuDrawIndexedPrimitives(SDL_GPURenderPass *renderPass, Uint32 numIndices, Uint32 numInstances, Uint32 firstIndex, Sint32 vertexOffset, Uint32 firstInstance) {
|
|
_backend->drawIndexedPrimitives(renderPass, numIndices, numInstances, firstIndex, vertexOffset, firstInstance);
|
|
}
|
|
|
|
|
|
void rgpuDrawPrimitives(SDL_GPURenderPass *renderPass, Uint32 numVertices, Uint32 numInstances, Uint32 firstVertex, Uint32 firstInstance) {
|
|
_backend->drawPrimitives(renderPass, numVertices, numInstances, firstVertex, firstInstance);
|
|
}
|
|
|
|
|
|
void rgpuEndCopyPass(SDL_GPUCopyPass *copyPass) {
|
|
_backend->endCopyPass(copyPass);
|
|
}
|
|
|
|
|
|
void rgpuEndRenderPass(SDL_GPURenderPass *renderPass) {
|
|
_backend->endRenderPass(renderPass);
|
|
}
|
|
|
|
|
|
void rgpuGenerateMipmapsForTexture(SDL_GPUCommandBuffer *commandBuffer, SDL_GPUTexture *texture) {
|
|
_backend->generateMipmapsForTexture(commandBuffer, texture);
|
|
}
|
|
|
|
|
|
const char *rgpuGetDeviceDriver(SDL_GPUDevice *device) {
|
|
return _backend->getDeviceDriver(device);
|
|
}
|
|
|
|
|
|
SDL_GPUShaderFormat rgpuGetShaderFormats(SDL_GPUDevice *device) {
|
|
return _backend->getShaderFormats(device);
|
|
}
|
|
|
|
|
|
SDL_GPUTextureFormat rgpuGetSwapchainTextureFormat(SDL_GPUDevice *device, SDL_Window *window) {
|
|
return _backend->getSwapchainTextureFormat(device, window);
|
|
}
|
|
|
|
|
|
SDL_GPUTextureFormat rgpuGetTextureFormatFromPixelFormat(SDL_PixelFormat format) {
|
|
return _backend->getTextureFormatFromPixelFormat(format);
|
|
}
|
|
|
|
|
|
void *rgpuMapTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer, bool cycle) {
|
|
return _backend->mapTransferBuffer(device, transferBuffer, cycle);
|
|
}
|
|
|
|
|
|
void rgpuPushFragmentUniformData(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length) {
|
|
_backend->pushFragmentUniformData(commandBuffer, slotIndex, data, length);
|
|
}
|
|
|
|
|
|
void rgpuPushVertexUniformData(SDL_GPUCommandBuffer *commandBuffer, Uint32 slotIndex, const void *data, Uint32 length) {
|
|
_backend->pushVertexUniformData(commandBuffer, slotIndex, data, length);
|
|
}
|
|
|
|
|
|
void rgpuReleaseBuffer(SDL_GPUDevice *device, SDL_GPUBuffer *buffer) {
|
|
_backend->releaseBuffer(device, buffer);
|
|
}
|
|
|
|
|
|
void rgpuReleaseGraphicsPipeline(SDL_GPUDevice *device, SDL_GPUGraphicsPipeline *graphicsPipeline) {
|
|
_backend->releaseGraphicsPipeline(device, graphicsPipeline);
|
|
}
|
|
|
|
|
|
void rgpuReleaseSampler(SDL_GPUDevice *device, SDL_GPUSampler *sampler) {
|
|
_backend->releaseSampler(device, sampler);
|
|
}
|
|
|
|
|
|
void rgpuReleaseShader(SDL_GPUDevice *device, SDL_GPUShader *shader) {
|
|
_backend->releaseShader(device, shader);
|
|
}
|
|
|
|
|
|
void rgpuReleaseTexture(SDL_GPUDevice *device, SDL_GPUTexture *texture) {
|
|
_backend->releaseTexture(device, texture);
|
|
}
|
|
|
|
|
|
void rgpuReleaseTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer) {
|
|
_backend->releaseTransferBuffer(device, transferBuffer);
|
|
}
|
|
|
|
|
|
void rgpuSetScissor(SDL_GPURenderPass *renderPass, const SDL_Rect *scissor) {
|
|
_backend->setScissor(renderPass, scissor);
|
|
}
|
|
|
|
|
|
void rgpuSetStencilReference(SDL_GPURenderPass *renderPass, Uint8 reference) {
|
|
_backend->setStencilReference(renderPass, reference);
|
|
}
|
|
|
|
|
|
bool rgpuSubmitCommandBuffer(SDL_GPUCommandBuffer *commandBuffer) {
|
|
bool ok = _backend->submitCommandBuffer(commandBuffer);
|
|
|
|
renderEnd();
|
|
return ok;
|
|
}
|
|
|
|
|
|
bool rgpuTextureSupportsFormat(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUTextureType type, SDL_GPUTextureUsageFlags usage) {
|
|
// A format of our own naming (ETC2) is the GLES backend's alone; SDL's function would index
|
|
// its tables past the end with it.
|
|
if ((format >= RGPU_TEXTUREFORMAT_PRIVATE_FIRST) && (_api != RENDER_GLES)) {
|
|
return false;
|
|
}
|
|
return _backend->textureSupportsFormat(device, format, type, usage);
|
|
}
|
|
|
|
|
|
bool rgpuTextureSupportsSampleCount(SDL_GPUDevice *device, SDL_GPUTextureFormat format, SDL_GPUSampleCount sampleCount) {
|
|
return _backend->textureSupportsSampleCount(device, format, sampleCount);
|
|
}
|
|
|
|
|
|
void rgpuUnmapTransferBuffer(SDL_GPUDevice *device, SDL_GPUTransferBuffer *transferBuffer) {
|
|
_backend->unmapTransferBuffer(device, transferBuffer);
|
|
}
|
|
|
|
|
|
void rgpuUploadToBuffer(SDL_GPUCopyPass *copyPass, const SDL_GPUTransferBufferLocation *source, const SDL_GPUBufferRegion *destination, bool cycle) {
|
|
_backend->uploadToBuffer(copyPass, source, destination, cycle);
|
|
}
|
|
|
|
|
|
void rgpuUploadToTexture(SDL_GPUCopyPass *copyPass, const SDL_GPUTextureTransferInfo *source, const SDL_GPUTextureRegion *destination, bool cycle) {
|
|
_backend->uploadToTexture(copyPass, source, destination, cycle);
|
|
}
|