From 2aee105b43f77ef0a84e7d9c5acab29a9ca0d7b5 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 13 Mar 2025 16:41:58 -0700 Subject: [PATCH] Added support for custom shaders with the GPU renderer Added an example of MSDF font rendering with the SDL 2D renderer --- include/SDL3/SDL_render.h | 112 ++++++++ src/dynapi/SDL_dynapi.sym | 4 + src/dynapi/SDL_dynapi_overrides.h | 4 + src/dynapi/SDL_dynapi_procs.h | 4 + src/render/SDL_render.c | 153 +++++++++++ src/render/SDL_sysrender.h | 32 +++ src/render/gpu/SDL_pipeline_gpu.c | 32 +-- src/render/gpu/SDL_pipeline_gpu.h | 1 + src/render/gpu/SDL_render_gpu.c | 31 ++- src/render/gpu/SDL_shaders_gpu.c | 6 + src/render/gpu/SDL_shaders_gpu.h | 1 + test/CMakeLists.txt | 3 +- test/msdf_font.bmp | Bin 0 -> 196662 bytes test/msdf_font.csv | 95 +++++++ test/testgpurender_msdf.c | 321 ++++++++++++++++++++++ test/testgpurender_msdf.frag.dxil.h | 399 ++++++++++++++++++++++++++++ test/testgpurender_msdf.frag.hlsl | 37 +++ test/testgpurender_msdf.frag.msl.h | 97 +++++++ test/testgpurender_msdf.frag.spv.h | 162 +++++++++++ 19 files changed, 1460 insertions(+), 34 deletions(-) create mode 100755 test/msdf_font.bmp create mode 100755 test/msdf_font.csv create mode 100644 test/testgpurender_msdf.c create mode 100644 test/testgpurender_msdf.frag.dxil.h create mode 100644 test/testgpurender_msdf.frag.hlsl create mode 100644 test/testgpurender_msdf.frag.msl.h create mode 100644 test/testgpurender_msdf.frag.spv.h diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 432cacfc4d..242556c127 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -59,6 +59,7 @@ #include #include #include +#include #include /* Set up for C function definitions, even when using C++ */ @@ -2709,6 +2710,117 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetDefaultTextureScaleMode(SDL_Renderer *re */ extern SDL_DECLSPEC bool SDLCALL SDL_GetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode *scale_mode); +/** + * GPU render state description. + * + * This structure should be initialized using SDL_INIT_INTERFACE(). + * + * \since This struct is available since SDL 3.4.0. + * + * \sa SDL_CreateGPURenderState + */ +typedef struct SDL_GPURenderStateDesc +{ + Uint32 version; /**< the version of this interface */ + + SDL_GPUShader *fragment_shader; /**< The fragment shader to use when this render state is active */ + + Sint32 num_sampler_bindings; /**< The number of additional fragment samplers to bind when this render state is active */ + const SDL_GPUTextureSamplerBinding *sampler_bindings; /** Additional fragment samplers to bind when this render state is active */ + + Sint32 num_storage_textures; /**< The number of storage textures to bind when this render state is active */ + SDL_GPUTexture *const *storage_textures; /** Storage textures to bind when this render state is active */ + + Sint32 num_storage_buffers; /**< The number of storage buffers to bind when this render state is active */ + SDL_GPUBuffer *const *storage_buffers; /** Storage buffers to bind when this render state is active */ +} SDL_GPURenderStateDesc; + +/* Check the size of SDL_GPURenderStateDesc + * + * If this assert fails, either the compiler is padding to an unexpected size, + * or the interface has been updated and this should be updated to match and + * the code using this interface should be updated to handle the old version. + */ +SDL_COMPILE_TIME_ASSERT(SDL_GPURenderStateDesc_SIZE, + (sizeof(void *) == 4 && sizeof(SDL_GPURenderStateDesc) == 32) || + (sizeof(void *) == 8 && sizeof(SDL_GPURenderStateDesc) == 64)); + +/** + * A custom GPU render state. + * + * \since This struct is available since SDL 3.4.0. + * + * \sa SDL_CreateGPURenderState + * \sa SDL_SetGPURenderStateFragmentUniformData + * \sa SDL_SetRenderGPUState + * \sa SDL_DestroyGPURenderState + */ +typedef struct SDL_GPURenderState SDL_GPURenderState; + +/** + * Create custom GPU render state. + * + * \param renderer the renderer to use. + * \param desc GPU render state description, initialized using SDL_INIT_INTERFACE(). + * \returns a custom GPU render state or NULL on failure; call SDL_GetError() for more information. + * + * \threadsafety This function should be called on the thread that created the renderer. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_SetGPURenderStateFragmentUniformData + * \sa SDL_SetRenderGPUState + * \sa SDL_DestroyGPURenderState + */ +extern SDL_DECLSPEC SDL_GPURenderState * SDLCALL SDL_CreateGPURenderState(SDL_Renderer *renderer, SDL_GPURenderStateDesc *desc); + +/** + * Set fragment shader uniform variables in a custom GPU render state. + * + * The data is copied and will be pushed using SDL_PushGPUFragmentUniformData() during draw call execution. + * + * \param state the state to modify. + * \param slot_index the fragment uniform slot to push data to. + * \param data client data to write. + * \param length the length of the data to write. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the renderer. + * + * \since This function is available since SDL 3.4.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_SetGPURenderStateFragmentUniformData(SDL_GPURenderState *state, Uint32 slot_index, const void *data, Uint32 length); + +/** + * Set custom GPU render state. + * + * This function sets custom GPU render state for subsequent draw calls. This allows using custom shaders with the GPU renderer. + * + * \param renderer the renderer to use. + * \param state the state to to use, or NULL to clear custom GPU render state. + * \returns true on success or false on failure; call SDL_GetError() for more + * information. + * + * \threadsafety This function should be called on the thread that created the renderer. + * + * \since This function is available since SDL 3.4.0. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_SetRenderGPUState(SDL_Renderer *renderer, SDL_GPURenderState *state); + +/** + * Destroy custom GPU render state. + * + * \param state the state to destroy. + * + * \threadsafety This function should be called on the thread that created the renderer. + * + * \since This function is available since SDL 3.4.0. + * + * \sa SDL_CreateGPURenderState + */ +extern SDL_DECLSPEC void SDLCALL SDL_DestroyGPURenderState(SDL_GPURenderState *state); + /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index ecf08a7d93..d40edd2320 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -1238,6 +1238,10 @@ SDL3_0.0.0 { SDL_RenderTexture9GridTiled; SDL_SetDefaultTextureScaleMode; SDL_GetDefaultTextureScaleMode; + SDL_CreateGPURenderState; + SDL_SetGPURenderStateFragmentUniformData; + SDL_SetRenderGPUState; + SDL_DestroyGPURenderState; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 2b35173adc..31f8229eaf 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -1263,3 +1263,7 @@ #define SDL_RenderTexture9GridTiled SDL_RenderTexture9GridTiled_REAL #define SDL_SetDefaultTextureScaleMode SDL_SetDefaultTextureScaleMode_REAL #define SDL_GetDefaultTextureScaleMode SDL_GetDefaultTextureScaleMode_REAL +#define SDL_CreateGPURenderState SDL_CreateGPURenderState_REAL +#define SDL_SetGPURenderStateFragmentUniformData SDL_SetGPURenderStateFragmentUniformData_REAL +#define SDL_SetRenderGPUState SDL_SetRenderGPUState_REAL +#define SDL_DestroyGPURenderState SDL_DestroyGPURenderState_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 6ea49ff7c9..c87a014dfb 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1271,3 +1271,7 @@ SDL_DYNAPI_PROC(bool,SDL_SetRelativeMouseTransform,(SDL_MouseMotionTransformCall SDL_DYNAPI_PROC(bool,SDL_RenderTexture9GridTiled,(SDL_Renderer *a,SDL_Texture *b,const SDL_FRect *c,float d,float e,float f,float g,float h,const SDL_FRect *i,float j),(a,b,c,d,e,f,g,h,i,j),return) SDL_DYNAPI_PROC(bool,SDL_SetDefaultTextureScaleMode,(SDL_Renderer *a,SDL_ScaleMode b),(a,b),return) SDL_DYNAPI_PROC(bool,SDL_GetDefaultTextureScaleMode,(SDL_Renderer *a,SDL_ScaleMode *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_GPURenderState*,SDL_CreateGPURenderState,(SDL_Renderer *a,SDL_GPURenderStateDesc *b),(a,b),return) +SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateFragmentUniformData,(SDL_GPURenderState *a,Uint32 b,const void *c,Uint32 d),(a,b,c,d),return) +SDL_DYNAPI_PROC(bool,SDL_SetRenderGPUState,(SDL_Renderer *a,SDL_GPURenderState *b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_DestroyGPURenderState,(SDL_GPURenderState *a),(a),) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index a03796a4e6..8622e3a1af 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -346,6 +346,16 @@ static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture) return true; } +static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state) +{ + SDL_Renderer *renderer = state->renderer; + if (state->last_command_generation == renderer->render_command_generation) { + // the current command queue depends on this state, flush the queue now before it changes + return FlushRenderCommands(renderer); + } + return true; +} + bool SDL_FlushRenderer(SDL_Renderer *renderer) { if (!FlushRenderCommands(renderer)) { @@ -577,6 +587,10 @@ static SDL_RenderCommand *PrepQueueCmdDraw(SDL_Renderer *renderer, const SDL_Ren cmd->data.draw.texture_scale_mode = texture->scaleMode; } cmd->data.draw.texture_address_mode = SDL_TEXTURE_ADDRESS_CLAMP; + cmd->data.draw.gpu_render_state = renderer->gpu_render_state; + if (renderer->gpu_render_state) { + renderer->gpu_render_state->last_command_generation = renderer->render_command_generation; + } } } return cmd; @@ -5824,3 +5838,142 @@ bool SDL_GetDefaultTextureScaleMode(SDL_Renderer *renderer, SDL_ScaleMode *scale } return true; } + +SDL_GPURenderState *SDL_CreateGPURenderState(SDL_Renderer *renderer, SDL_GPURenderStateDesc *desc) +{ + CHECK_RENDERER_MAGIC(renderer, false); + + if (!desc) { + SDL_InvalidParamError("desc"); + return NULL; + } + + if (desc->version < sizeof(*desc)) { + // Update this to handle older versions of this interface + SDL_SetError("Invalid desc, should be initialized with SDL_INIT_INTERFACE()"); + return NULL; + } + + if (!desc->fragment_shader) { + SDL_SetError("desc->fragment_shader is required"); + return NULL; + } + + SDL_GPUDevice *device = (SDL_GPUDevice *)SDL_GetPointerProperty(renderer->props, SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL); + if (!device) { + SDL_SetError("Renderer isn't associated with a GPU device"); + return NULL; + } + + SDL_GPURenderState *state = (SDL_GPURenderState *)SDL_calloc(1, sizeof(*state)); + if (!state) { + return NULL; + } + + state->renderer = renderer; + state->fragment_shader = desc->fragment_shader; + + if (desc->num_sampler_bindings > 0) { + state->sampler_bindings = (SDL_GPUTextureSamplerBinding *)SDL_calloc(desc->num_sampler_bindings, sizeof(*state->sampler_bindings)); + if (!state->sampler_bindings) { + SDL_DestroyGPURenderState(state); + return NULL; + } + SDL_memcpy(state->sampler_bindings, desc->sampler_bindings, desc->num_sampler_bindings * sizeof(*state->sampler_bindings)); + state->num_sampler_bindings = desc->num_sampler_bindings; + } + + if (desc->num_storage_textures > 0) { + state->storage_textures = (SDL_GPUTexture **)SDL_calloc(desc->num_storage_textures, sizeof(*state->storage_textures)); + if (!state->storage_textures) { + SDL_DestroyGPURenderState(state); + return NULL; + } + SDL_memcpy(state->storage_textures, desc->storage_textures, desc->num_storage_textures * sizeof(*state->storage_textures)); + state->num_storage_textures = desc->num_storage_textures; + } + + if (desc->num_storage_buffers > 0) { + state->storage_buffers = (SDL_GPUBuffer **)SDL_calloc(desc->num_storage_buffers, sizeof(*state->storage_buffers)); + if (!state->storage_buffers) { + SDL_DestroyGPURenderState(state); + return NULL; + } + SDL_memcpy(state->storage_buffers, desc->storage_buffers, desc->num_storage_buffers * sizeof(*state->storage_buffers)); + state->num_storage_buffers = desc->num_storage_buffers; + } + + return state; +} + +bool SDL_SetGPURenderStateFragmentUniformData(SDL_GPURenderState *state, Uint32 slot_index, const void *data, Uint32 length) +{ + if (!state) { + return SDL_InvalidParamError("state"); + } + + if (!FlushRenderCommandsIfGPURenderStateNeeded(state)) { + return false; + } + + for (int i = 0; i < state->num_uniform_buffers; i++) { + SDL_GPURenderStateUniformBuffer *buffer = &state->uniform_buffers[i]; + if (buffer->slot_index == slot_index) { + void *new_data = SDL_realloc(buffer->data, length); + if (!new_data) { + return false; + } + SDL_memcpy(new_data, data, length); + buffer->data = new_data; + buffer->length = length; + return true; + } + } + + SDL_GPURenderStateUniformBuffer *buffers = (SDL_GPURenderStateUniformBuffer *)SDL_realloc(state->uniform_buffers, (state->num_uniform_buffers + 1) * sizeof(*state->uniform_buffers)); + if (!buffers) { + return false; + } + + SDL_GPURenderStateUniformBuffer *buffer = &buffers[state->num_uniform_buffers]; + buffer->slot_index = slot_index; + buffer->length = length; + buffer->data = SDL_malloc(length); + if (!buffer->data) { + SDL_free(buffers); + return false; + } + SDL_memcpy(buffer->data, data, length); + + state->uniform_buffers = buffers; + ++state->num_uniform_buffers; + return true; +} + +bool SDL_SetRenderGPUState(SDL_Renderer *renderer, SDL_GPURenderState *state) +{ + CHECK_RENDERER_MAGIC(renderer, false); + + renderer->gpu_render_state = state; + return true; +} + +void SDL_DestroyGPURenderState(SDL_GPURenderState *state) +{ + if (!state) { + return; + } + + FlushRenderCommandsIfGPURenderStateNeeded(state); + + if (state->num_uniform_buffers > 0) { + for (int i = 0; i < state->num_uniform_buffers; i++) { + SDL_free(state->uniform_buffers[i].data); + } + SDL_free(state->uniform_buffers); + } + SDL_free(state->sampler_bindings); + SDL_free(state->storage_textures); + SDL_free(state->storage_buffers); + SDL_free(state); +} diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 58692388ff..2e6cc76cde 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -118,6 +118,36 @@ struct SDL_Texture SDL_Texture *next; }; +// Define the GPU render state structure +typedef struct SDL_GPURenderStateUniformBuffer +{ + Uint32 slot_index; + void *data; + Uint32 length; +} SDL_GPURenderStateUniformBuffer; + +// Define the GPU render state structure +struct SDL_GPURenderState +{ + SDL_Renderer *renderer; + + Uint32 last_command_generation; // last command queue generation this state was in. + + SDL_GPUShader *fragment_shader; + + int num_sampler_bindings; + SDL_GPUTextureSamplerBinding *sampler_bindings; + + int num_storage_textures; + SDL_GPUTexture **storage_textures; + + int num_storage_buffers; + SDL_GPUBuffer **storage_buffers; + + int num_uniform_buffers; + SDL_GPURenderStateUniformBuffer *uniform_buffers; +}; + typedef enum { SDL_RENDERCMD_NO_OP, @@ -158,6 +188,7 @@ typedef struct SDL_RenderCommand SDL_Texture *texture; SDL_ScaleMode texture_scale_mode; SDL_TextureAddressMode texture_address_mode; + SDL_GPURenderState *gpu_render_state; } draw; struct { @@ -282,6 +313,7 @@ struct SDL_Renderer SDL_FColor color; /**< Color for drawing operations values */ SDL_BlendMode blendMode; /**< The drawing blend mode */ SDL_TextureAddressMode texture_address_mode; + SDL_GPURenderState *gpu_render_state; SDL_RenderCommand *render_commands; SDL_RenderCommand *render_commands_tail; diff --git a/src/render/gpu/SDL_pipeline_gpu.c b/src/render/gpu/SDL_pipeline_gpu.c index 078337401b..31da279777 100644 --- a/src/render/gpu/SDL_pipeline_gpu.c +++ b/src/render/gpu/SDL_pipeline_gpu.c @@ -27,40 +27,10 @@ #include "../SDL_sysrender.h" -struct GPU_PipelineCacheKeyStruct -{ - Uint64 blend_mode : 28; - Uint64 frag_shader : 4; - Uint64 vert_shader : 4; - Uint64 attachment_format : 6; - Uint64 primitive_type : 3; -}; - -typedef union GPU_PipelineCacheKeyConverter -{ - struct GPU_PipelineCacheKeyStruct as_struct; - Uint64 as_uint64; -} GPU_PipelineCacheKeyConverter; - -SDL_COMPILE_TIME_ASSERT(GPU_PipelineCacheKeyConverter_Size, sizeof(GPU_PipelineCacheKeyConverter) <= sizeof(Uint64)); - static Uint32 SDLCALL HashPipelineCacheKey(void *userdata, const void *key) { const GPU_PipelineParameters *params = (const GPU_PipelineParameters *) key; - GPU_PipelineCacheKeyConverter cvt; - cvt.as_uint64 = 0; - cvt.as_struct.blend_mode = params->blend_mode; - cvt.as_struct.frag_shader = params->frag_shader; - cvt.as_struct.vert_shader = params->vert_shader; - cvt.as_struct.attachment_format = params->attachment_format; - cvt.as_struct.primitive_type = params->primitive_type; - - // 64-bit uint hash function stolen from taisei (which stole it from somewhere else) - Uint64 x = cvt.as_uint64; - x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); - x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); - x = x ^ (x >> 31); - return (Uint32)(x & 0xffffffff); + return SDL_murmur3_32(params, sizeof(*params), 0); } static bool SDLCALL MatchPipelineCacheKey(void *userdata, const void *a, const void *b) diff --git a/src/render/gpu/SDL_pipeline_gpu.h b/src/render/gpu/SDL_pipeline_gpu.h index c3fc39bc69..96e7bd9433 100644 --- a/src/render/gpu/SDL_pipeline_gpu.h +++ b/src/render/gpu/SDL_pipeline_gpu.h @@ -33,6 +33,7 @@ typedef struct GPU_PipelineParameters GPU_VertexShaderID vert_shader; SDL_GPUTextureFormat attachment_format; SDL_GPUPrimitiveType primitive_type; + SDL_GPUShader *custom_frag_shader; } GPU_PipelineParameters; typedef struct GPU_PipelineCache diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index d7604f9290..da28b7e55b 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -541,6 +541,8 @@ static void Draw( } SDL_GPURenderPass *pass = data->state.render_pass; + SDL_GPURenderState *custom_state = cmd->data.draw.gpu_render_state; + SDL_GPUShader *custom_frag_shader = custom_state ? custom_state->fragment_shader : NULL; GPU_VertexShaderID v_shader; GPU_FragmentShaderID f_shader; @@ -570,12 +572,18 @@ static void Draw( f_shader = FRAG_SHADER_COLOR; } + if (custom_frag_shader) { + f_shader = FRAG_SHADER_TEXTURE_CUSTOM; + data->shaders.frag_shaders[FRAG_SHADER_TEXTURE_CUSTOM] = custom_frag_shader; + } + GPU_PipelineParameters pipe_params; SDL_zero(pipe_params); pipe_params.blend_mode = cmd->data.draw.blend; pipe_params.vert_shader = v_shader; pipe_params.frag_shader = f_shader; pipe_params.primitive_type = prim; + pipe_params.custom_frag_shader = custom_frag_shader; if (data->state.render_target) { pipe_params.attachment_format = ((GPU_TextureData *)data->state.render_target->internal)->format; @@ -590,15 +598,34 @@ static void Draw( SDL_BindGPUGraphicsPipeline(pass, pipe); + Uint32 sampler_slot = 0; if (cmd->data.draw.texture) { GPU_TextureData *tdata = (GPU_TextureData *)cmd->data.draw.texture->internal; SDL_GPUTextureSamplerBinding sampler_bind; SDL_zero(sampler_bind); sampler_bind.sampler = *SamplerPointer(data, cmd->data.draw.texture_address_mode, cmd->data.draw.texture_scale_mode); sampler_bind.texture = tdata->texture; - SDL_BindGPUFragmentSamplers(pass, 0, &sampler_bind, 1); + SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); + } + if (custom_state) { + if (custom_state->num_sampler_bindings > 0) { + SDL_BindGPUFragmentSamplers(pass, sampler_slot, custom_state->sampler_bindings, custom_state->num_sampler_bindings); + } + if (custom_state->num_storage_textures > 0) { + SDL_BindGPUFragmentStorageTextures(pass, 0, custom_state->storage_textures, custom_state->num_storage_textures); + } + if (custom_state->num_storage_buffers > 0) { + SDL_BindGPUFragmentStorageBuffers(pass, 0, custom_state->storage_buffers, custom_state->num_storage_buffers); + } + if (custom_state->num_uniform_buffers > 0) { + for (int i = 0; i < custom_state->num_uniform_buffers; i++) { + SDL_GPURenderStateUniformBuffer *ub = &custom_state->uniform_buffers[i]; + SDL_PushGPUFragmentUniformData(data->state.command_buffer, ub->slot_index, ub->data, ub->length); + } + } + } else { + PushFragmentUniforms(data, cmd); } - PushFragmentUniforms(data, cmd); SDL_GPUBufferBinding buffer_bind; SDL_zero(buffer_bind); diff --git a/src/render/gpu/SDL_shaders_gpu.c b/src/render/gpu/SDL_shaders_gpu.c index 04e06f69db..f09a40e468 100644 --- a/src/render/gpu/SDL_shaders_gpu.c +++ b/src/render/gpu/SDL_shaders_gpu.c @@ -196,6 +196,9 @@ bool GPU_InitShaders(GPU_Shaders *shaders, SDL_GPUDevice *device) } for (int i = 0; i < SDL_arraysize(frag_shader_sources); ++i) { + if (i == FRAG_SHADER_TEXTURE_CUSTOM) { + continue; + } shaders->frag_shaders[i] = CompileShader( &frag_shader_sources[i], device, SDL_GPU_SHADERSTAGE_FRAGMENT); if (shaders->frag_shaders[i] == NULL) { @@ -215,6 +218,9 @@ void GPU_ReleaseShaders(GPU_Shaders *shaders, SDL_GPUDevice *device) } for (int i = 0; i < SDL_arraysize(shaders->frag_shaders); ++i) { + if (i == FRAG_SHADER_TEXTURE_CUSTOM) { + continue; + } SDL_ReleaseGPUShader(device, shaders->frag_shaders[i]); shaders->frag_shaders[i] = NULL; } diff --git a/src/render/gpu/SDL_shaders_gpu.h b/src/render/gpu/SDL_shaders_gpu.h index d3190fa227..9dc03b6efa 100644 --- a/src/render/gpu/SDL_shaders_gpu.h +++ b/src/render/gpu/SDL_shaders_gpu.h @@ -44,6 +44,7 @@ typedef enum FRAG_SHADER_TEXTURE_RGBA, FRAG_SHADER_TEXTURE_RGB_PIXELART, FRAG_SHADER_TEXTURE_RGBA_PIXELART, + FRAG_SHADER_TEXTURE_CUSTOM, NUM_FRAG_SHADERS, } GPU_FragmentShaderID; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3be04e215e..efbea0c485 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,7 +39,7 @@ add_library(sdltests_utils OBJECT ) target_link_libraries(sdltests_utils PRIVATE SDL3::Headers) -file(GLOB RESOURCE_FILES *.bmp *.wav *.hex moose.dat utf8.txt) +file(GLOB RESOURCE_FILES *.bmp *.wav *.csv *.hex moose.dat utf8.txt) option(SDLTEST_TRACKMEM "Run tests with --trackmem" OFF) @@ -348,6 +348,7 @@ add_sdl_test_executable(testgl SOURCES testgl.c) add_sdl_test_executable(testgles SOURCES testgles.c) add_sdl_test_executable(testgpu_simple_clear SOURCES testgpu_simple_clear.c) add_sdl_test_executable(testgpu_spinning_cube SOURCES testgpu_spinning_cube.c) +add_sdl_test_executable(testgpurender_msdf MAIN_CALLBACKS NEEDS_RESOURCES TESTUTILS SOURCES testgpurender_msdf.c) if(ANDROID) target_link_libraries(testgles PRIVATE GLESv1_CM) elseif(IOS OR TVOS) diff --git a/test/msdf_font.bmp b/test/msdf_font.bmp new file mode 100755 index 0000000000000000000000000000000000000000..43d7b72709cb41d0a1d487a2aa966dd0e4c8598e GIT binary patch literal 196662 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

)J@ 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 P{ZzX|P!1~s_v!F&%W;!P literal 0 HcmV?d00001 diff --git a/test/msdf_font.csv b/test/msdf_font.csv new file mode 100755 index 0000000000..28fa0d63c6 --- /dev/null +++ b/test/msdf_font.csv @@ -0,0 +1,95 @@ +32,0.259765625,0,-0,0,-0,0,256,0,256 +33,0.26416015625,0.04259963723256735,-0.74801901743264665,0.22009567526743265,0.063391442155309036,167.5,44.5,174.5,76.5 +34,0.3984375,0.034401000396196514,-0.74801901743264665,0.36403649960380352,-0.41838351822503961,242.5,175.5,255.5,188.5 +35,0.64599609375,-0.0068815929576070169,-0.74801901743264665,0.65238940545760693,0.038034865293185421,0.5,111.5,26.5,142.5 +36,0.57177734375,0.033055325128763823,-0.79873217115689388,0.54018686237123603,0.08874801901743265,142.5,0.5,162.5,35.5 +37,0.82666015625,0.020303136762083947,-0.77337559429477021,0.80635701948791605,0.038034865293185421,175.5,44.5,206.5,76.5 +38,0.728515625,0.021001510499207562,-0.77337559429477021,0.75634223950079238,0.038034865293185421,214.5,44.5,243.5,76.5 +39,0.21923828125,0.033793550663629157,-0.74801901743264665,0.18593301183637084,-0.41838351822503961,89.5,232.5,95.5,245.5 +40,0.294921875,0.012882577258320127,-0.74801901743264665,0.29180492274167991,0.19017432646592711,62.5,0.5,73.5,37.5 +41,0.294921875,0.0031169522583201267,-0.74801901743264665,0.28203929774167991,0.19017432646592711,74.5,0.5,85.5,37.5 +42,0.55078125,0.0079258648227020154,-0.79873217115689388,0.54041397892729792,-0.26624405705229792,234.5,78.5,255.5,99.5 +43,0.57177734375,0.018668052322702015,-0.62123613312202852,0.55115616642729792,-0.063391442155309036,22.5,232.5,43.5,254.5 +44,0.2587890625,0.011122520676505546,-0.16481774960380352,0.21397513557349446,0.16481774960380349,118.5,232.5,126.5,245.5 +45,0.32177734375,0.0089933513272583213,-0.34231378763866882,0.31327227367274169,-0.19017432646592711,242.5,189.5,254.5,195.5 +46,0.2626953125,0.04259963723256735,-0.13946117274167988,0.22009567526743265,0.063391442155309036,138.5,232.5,145.5,240.5 +47,0.36669921875,-0.019747146146988907,-0.74801901743264665,0.38595808364698891,0.038034865293185421,95.5,143.5,111.5,174.5 +48,0.57177734375,0.019400474197702015,-0.77337559429477021,0.55188858830229792,0.038034865293185421,0.5,78.5,21.5,110.5 +49,0.57177734375,0.055885375396196514,-0.74801901743264665,0.38552087460380352,0.038034865293185421,137.5,143.5,150.5,174.5 +50,0.57177734375,0.016959067947702015,-0.77337559429477021,0.54944718205229792,0.038034865293185421,22.5,78.5,43.5,110.5 +51,0.57177734375,0.012564536697702015,-0.77337559429477021,0.54505265080229792,0.038034865293185421,44.5,78.5,65.5,110.5 +52,0.57177734375,-0.0044912589144215989,-0.74801901743264665,0.57871000891442159,0.038034865293185421,232.5,143.5,255.5,174.5 +53,0.57177734375,0.035740872003763823,-0.74801901743264665,0.54287240924623603,0.038034865293185421,235.5,111.5,255.5,142.5 +54,0.57177734375,0.023795005447702015,-0.77337559429477021,0.55628311955229792,0.038034865293185421,66.5,78.5,87.5,110.5 +55,0.57177734375,0.017203208572702015,-0.74801901743264665,0.54969132267729792,0.038034865293185421,82.5,175.5,103.5,206.5 +56,0.57177734375,0.018912192947702015,-0.77337559429477021,0.55140030705229792,0.038034865293185421,88.5,78.5,109.5,110.5 +57,0.57177734375,0.017935630447702015,-0.77337559429477021,0.55042374455229792,0.038034865293185421,110.5,78.5,131.5,110.5 +58,0.2626953125,0.04259963723256735,-0.59587955625990485,0.22009567526743265,0.063391442155309036,130.5,175.5,137.5,201.5 +59,0.2626953125,-0.0044854552545562608,-0.59587955625990485,0.22372373650455626,0.16481774960380349,104.5,175.5,113.5,205.5 +60,0.57177734375,0.018912192947702015,-0.64659270998415219,0.55140030705229792,-0.08874801901743265,0.5,232.5,21.5,254.5 +61,0.57177734375,0.018912192947702015,-0.51980982567353418,0.55140030705229792,-0.19017432646592711,96.5,232.5,117.5,245.5 +62,0.57177734375,0.018912192947702015,-0.64659270998415219,0.55140030705229792,-0.08874801901743265,44.5,232.5,65.5,254.5 +63,0.431640625,-0.018492394884112522,-0.77337559429477021,0.43792598863411253,0.063391442155309036,22.5,44.5,40.5,77.5 +64,0.896484375,0.030102809899960288,-0.74801901743264665,0.86686984635003961,0.13946117274167988,163.5,0.5,196.5,35.5 +65,0.63232421875,-0.026639959513668827,-0.74801901743264665,0.65798761576366882,0.038034865293185421,23.5,175.5,50.5,206.5 +66,0.64599609375,0.065804217016640215,-0.74801901743264665,0.6236489079833597,0.038034865293185421,0.5,175.5,22.5,206.5 +67,0.6298828125,0.025799202654516594,-0.77337559429477021,0.63435704734548326,0.038034865293185421,132.5,78.5,156.5,110.5 +68,0.7255859375,0.064146304848454794,-0.74801901743264665,0.69806072640154515,0.038034865293185421,189.5,143.5,214.5,174.5 +69,0.5556640625,0.06817752699088743,-0.74801901743264665,0.52459591050911247,0.038034865293185421,170.5,143.5,188.5,174.5 +70,0.51611328125,0.06817752699088743,-0.74801901743264665,0.52459591050911247,0.038034865293185421,151.5,143.5,169.5,174.5 +71,0.72705078125,0.027053953917392983,-0.77337559429477021,0.68632495233260693,0.038034865293185421,157.5,78.5,183.5,110.5 +72,0.7373046875,0.063885140154516601,-0.74801901743264665,0.67244298484548326,0.038034865293185421,112.5,143.5,136.5,174.5 +73,0.279296875,0.063090425663629157,-0.74801901743264665,0.21522988683637084,0.038034865293185421,244.5,44.5,250.5,75.5 +74,0.2685546875,-0.11647790585380349,-0.74801901743264665,0.21315759335380352,0.21553090332805072,48.5,0.5,61.5,38.5 +75,0.6123046875,0.063379834835578408,-0.74801901743264665,0.64658110266442159,0.038034865293185421,45.5,143.5,68.5,174.5 +76,0.52197265625,0.069398230115887444,-0.74801901743264665,0.52581661363411247,0.038034865293185421,26.5,143.5,44.5,174.5 +77,0.8994140625,0.069358378318145758,-0.74801901743264665,0.83005568418185416,0.038034865293185421,51.5,175.5,81.5,206.5 +78,0.7529296875,0.059751773598454787,-0.74801901743264665,0.69366619515154515,0.038034865293185421,0.5,143.5,25.5,174.5 +79,0.77783203125,0.033923939555269372,-0.77337559429477021,0.74390809169473049,0.038034865293185421,184.5,78.5,212.5,110.5 +80,0.6015625,0.070164700128763829,-0.74801901743264665,0.57729623737123603,0.038034865293185421,191.5,111.5,211.5,142.5 +81,0.77783203125,0.033923939555269372,-0.77337559429477021,0.74390809169473049,0.21553090332805072,19.5,0.5,47.5,39.5 +82,0.6171875,0.070198748266640215,-0.74801901743264665,0.6280434392333597,0.038034865293185421,129.5,111.5,151.5,142.5 +83,0.54833984375,0.022313137628763823,-0.77337559429477021,0.52944467487123603,0.038034865293185421,213.5,78.5,233.5,110.5 +84,0.55078125,-0.016942430789421599,-0.74801901743264665,0.56625883703942159,0.038034865293185421,79.5,111.5,102.5,142.5 +85,0.72900390625,0.059734749529516594,-0.74801901743264665,0.66829259422048326,0.038034865293185421,54.5,111.5,78.5,142.5 +86,0.59619140625,-0.031539796082607013,-0.74801901743264665,0.62773120233260693,0.038034865293185421,27.5,111.5,53.5,142.5 +87,0.92333984375,-0.020105038505348741,-0.74801901743264665,0.94344488225534862,0.038034865293185421,152.5,111.5,190.5,142.5 +88,0.57763671875,-0.028382992026545213,-0.74801901743264665,0.60553142952654515,0.038034865293185421,69.5,143.5,94.5,174.5 +89,0.55908203125,-0.037416195151545213,-0.74801901743264665,0.59649822640154515,0.038034865293185421,103.5,111.5,128.5,142.5 +90,0.572265625,0.0069663263916402082,-0.74801901743264665,0.5648110173583597,0.038034865293185421,212.5,111.5,234.5,142.5 +91,0.3271484375,0.052189217883320121,-0.74801901743264665,0.33111156336667991,0.19017432646592711,102.5,0.5,113.5,37.5 +92,0.36669921875,-0.019258864896988907,-0.74801901743264665,0.38644636489698891,0.038034865293185421,215.5,143.5,231.5,174.5 +93,0.3271484375,-0.0037189852416798751,-0.74801901743264665,0.27520336024167991,0.19017432646592711,114.5,0.5,125.5,37.5 +94,0.57177734375,0.0072104670166402082,-0.74801901743264665,0.5650551579833597,-0.24088748019017434,66.5,232.5,88.5,252.5 +95,0.43798828125,-0.034571627996236129,0.063391442155309036,0.47255990924623614,0.19017432646592711,89.5,246.5,109.5,251.5 +96,0.27734375,0.012133131314381934,-0.79873217115689388,0.26569889993561807,-0.57052297939778129,127.5,232.5,137.5,241.5 +97,0.5556640625,0.019122285434825671,-0.57052297939778129,0.50089724581517436,0.038034865293185421,21.5,207.5,40.5,231.5 +98,0.61181640625,0.054556724197702015,-0.79873217115689388,0.58704483830229792,0.038034865293185421,41.5,44.5,62.5,77.5 +99,0.47900390625,0.022767370740887478,-0.57052297939778129,0.47918575425911253,0.038034865293185421,168.5,207.5,186.5,231.5 +100,0.61181640625,0.024527427322702015,-0.79873217115689388,0.55701554142729792,0.038034865293185421,63.5,44.5,84.5,77.5 +101,0.5615234375,0.028904934503763823,-0.57052297939778129,0.53603647174623603,0.038034865293185421,200.5,175.5,220.5,199.5 +102,0.33642578125,-0.017288715828050714,-0.79873217115689388,0.41377309082805075,0.038034865293185421,128.5,44.5,145.5,77.5 +103,0.54296875,-0.022557665164421599,-0.59587955625990485,0.56064360266442159,0.26624405705229792,197.5,0.5,220.5,34.5 +104,0.61328125,0.055027981378763823,-0.79873217115689388,0.56215951862123603,0.038034865293185421,146.5,44.5,166.5,77.5 +105,0.25244140625,0.050883394413629157,-0.77337559429477021,0.20302285558637084,0.038034865293185421,207.5,44.5,213.5,76.5 +106,0.25244140625,-0.091592586172741686,-0.77337559429477021,0.21268633617274166,0.26624405705229792,6.5,0.5,18.5,41.5 +107,0.525390625,0.047947903253763823,-0.79873217115689388,0.55507944049623603,0.038034865293185421,107.5,44.5,127.5,77.5 +108,0.25244140625,0.050150972538629157,-0.79873217115689388,0.20229043371137084,0.038034865293185421,244.5,0.5,250.5,33.5 +109,0.92578125,0.059382660831022144,-0.57052297939778129,0.87079312041897783,0.038034865293185421,60.5,207.5,92.5,231.5 +110,0.61328125,0.055027981378763823,-0.57052297939778129,0.56215951862123603,0.038034865293185421,0.5,207.5,20.5,231.5 +111,0.6015625,0.022347185766640208,-0.57052297939778129,0.5801918767333597,0.038034865293185421,145.5,207.5,167.5,231.5 +112,0.61181640625,0.054556724197702015,-0.57052297939778129,0.58704483830229792,0.26624405705229792,85.5,44.5,106.5,77.5 +113,0.61181640625,0.024039146072702015,-0.57052297939778129,0.55652726017729792,0.26624405705229792,0.5,44.5,21.5,77.5 +114,0.40869140625,0.0493276266590729,-0.57052297939778129,0.42967627959092713,0.038034865293185421,138.5,175.5,153.5,199.5 +115,0.4765625,0.012757605115887478,-0.57052297939778129,0.46917598863411253,0.038034865293185421,41.5,207.5,59.5,231.5 +116,0.3564453125,-0.0156137795909271,-0.69730586370839942,0.36473487334092713,0.038034865293185421,114.5,175.5,129.5,204.5 +117,0.61328125,0.049901028253763823,-0.57052297939778129,0.55703256549623603,0.038034865293185421,221.5,175.5,241.5,199.5 +118,0.49951171875,-0.029166486108359792,-0.57052297939778129,0.5286782048583597,0.038034865293185421,154.5,175.5,176.5,199.5 +119,0.77490234375,-0.01825405791897786,-0.57052297939778129,0.79315640166897772,0.038034865293185421,93.5,207.5,125.5,231.5 +120,0.5234375,-0.017203595483359792,-0.57052297939778129,0.5406410954833597,0.038034865293185421,177.5,175.5,199.5,199.5 +121,0.5009765625,-0.027945782983359792,-0.57052297939778129,0.5298989079833597,0.26624405705229792,221.5,0.5,243.5,33.5 +122,0.46875,0.0059216676158874784,-0.57052297939778129,0.46234005113411253,0.038034865293185421,126.5,207.5,144.5,231.5 +123,0.375,-0.0048715920909271002,-0.74801901743264665,0.37547706084092713,0.19017432646592711,86.5,0.5,101.5,37.5 +124,0.54931640625,0.21077847971969096,-0.79873217115689377,0.33756136403030901,0.29160063391442159,0.5,0.5,5.5,43.5 +125,0.375,1.122040907289984e-05,-0.74801901743264665,0.38035987334092713,0.19017432646592711,126.5,0.5,141.5,37.5 +126,0.57177734375,0.018912192947702015,-0.44374009508716328,0.55140030705229792,-0.24088748019017434,234.5,100.5,255.5,108.5 diff --git a/test/testgpurender_msdf.c b/test/testgpurender_msdf.c new file mode 100644 index 0000000000..af25b986bd --- /dev/null +++ b/test/testgpurender_msdf.c @@ -0,0 +1,321 @@ +/* + Copyright (C) 1997-2025 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +#include "testutils.h" + +/* This font was created with: + * ./msdf-atlas-gen.exe -font OpenSans-VariableFont_wdth,wght.ttf -chars '[32,126]' -type msdf -potr -yorigin top -imageout msdf_font.bmp -csv msdf_font.csv + */ + +/* This is the distance field range in pixels used when generating the font atlas, defaults to 2 */ +#define DISTANCE_FIELD_RANGE 2.0f + +/* MSDF shaders */ +#include "testgpurender_msdf.frag.dxil.h" +#include "testgpurender_msdf.frag.msl.h" +#include "testgpurender_msdf.frag.spv.h" + +typedef struct +{ + float distance_field_range; + float texture_width; + float texture_height; + float padding; +} MSDFShaderUniforms; + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_Texture *font_texture = NULL; +static SDL_GPUDevice *device = NULL; +static SDL_GPUShader *shader = NULL; +static SDL_GPURenderState *render_state = NULL; + +typedef struct +{ + bool loaded; + SDL_FRect src; + SDL_FRect dst; + float advance; +} GlyphInfo; + +static GlyphInfo glyphs[128]; + +static bool LoadFontTexture(void) +{ + font_texture = LoadTexture(renderer, "msdf_font.bmp", false, NULL, NULL); + if (!font_texture) { + SDL_Log("Failed to create font texture: %s", SDL_GetError()); + return false; + } + SDL_SetTextureBlendMode(font_texture, SDL_BLENDMODE_BLEND); + + /* Set the font color, doesn't need to be done every frame */ + SDL_SetTextureColorMod(font_texture, 0, 0, 0); + + return true; +} + +static bool LoadFontLayout(void) +{ + const char *file = "msdf_font.csv"; + char *path; + int offset = 0, len, codepoint; + float src_left, src_top, src_right, src_bottom; + float dst_left, dst_top, dst_right, dst_bottom; + float advance; + char *font_layout; + + path = GetNearbyFilename(file); + if (path) { + font_layout = (char *)SDL_LoadFile(path, NULL); + SDL_free(path); + } else { + font_layout = (char *)SDL_LoadFile(file, NULL); + } + if (!font_layout) { + SDL_Log("Failed to load font layout: %s", SDL_GetError()); + return false; + } + + while (SDL_sscanf(&font_layout[offset], "%d,%f,%f,%f,%f,%f,%f,%f,%f,%f%n", + &codepoint, &advance, + &dst_left, &dst_top, &dst_right, &dst_bottom, + &src_left, &src_top, &src_right, &src_bottom, &len) == 10) { + if (codepoint >= 0 && codepoint < SDL_arraysize(glyphs)) { + GlyphInfo *glyph = &glyphs[codepoint]; + glyph->loaded = true; + glyph->src.x = src_left; + glyph->src.y = src_top; + glyph->src.w = src_right - src_left; + glyph->src.h = src_bottom - src_top; + glyph->dst.x = dst_left; + glyph->dst.y = dst_top; + glyph->dst.w = dst_right - dst_left; + glyph->dst.h = dst_bottom - dst_top; + glyph->advance = advance; + } + offset += len; + } + SDL_free(font_layout); + return true; +} + +static float MeasureText(const char *text, float font_size) +{ + float width = 0.0f; + + while (*text) { + GlyphInfo *glyph; + Uint32 codepoint = SDL_StepUTF8(&text, NULL); + if (codepoint >= SDL_arraysize(glyphs)) { + continue; + } + + glyph = &glyphs[codepoint]; + if (!glyph->loaded) { + continue; + } + width += (glyph->advance * font_size); + } + return width; +} + +static void RenderText(const char *text, float font_size, float x, float y) +{ + SDL_FRect dst; + + /* The y coordinate is actually the baseline for the text */ + + while (*text) { + GlyphInfo *glyph; + Uint32 codepoint = SDL_StepUTF8(&text, NULL); + if (codepoint >= SDL_arraysize(glyphs)) { + continue; + } + + glyph = &glyphs[codepoint]; + if (!glyph->loaded) { + continue; + } + + dst.x = x + glyph->dst.x * font_size; + dst.y = y + glyph->dst.y * font_size; + dst.w = glyph->dst.w * font_size; + dst.h = glyph->dst.h * font_size; + SDL_RenderTexture(renderer, font_texture, &glyph->src, &dst); + x += (glyph->advance * font_size); + } +} + +static bool InitGPURenderState(void) +{ + SDL_GPUShaderFormat formats; + SDL_GPUShaderCreateInfo info; + SDL_GPURenderStateDesc desc; + MSDFShaderUniforms uniforms; + + device = (SDL_GPUDevice *)SDL_GetPointerProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL); + if (!device) { + SDL_Log("Couldn't get GPU device"); + return false; + } + + formats = SDL_GetGPUShaderFormats(device); + if (formats == SDL_GPU_SHADERFORMAT_INVALID) { + SDL_Log("Couldn't get supported shader formats: %s", SDL_GetError()); + return false; + } + + SDL_zero(info); + if (formats & SDL_GPU_SHADERFORMAT_SPIRV) { + info.format = SDL_GPU_SHADERFORMAT_SPIRV; + info.code = testgpurender_msdf_frag_spv; + info.code_size = testgpurender_msdf_frag_spv_len; + info.entrypoint = "main"; + } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) { + info.format = SDL_GPU_SHADERFORMAT_DXIL; + info.code = testgpurender_msdf_frag_dxil; + info.code_size = testgpurender_msdf_frag_dxil_len; + info.entrypoint = "main"; + } else if (formats & SDL_GPU_SHADERFORMAT_MSL) { + info.format = SDL_GPU_SHADERFORMAT_MSL; + info.code = testgpurender_msdf_frag_msl; + info.code_size = testgpurender_msdf_frag_msl_len; + info.entrypoint = "main0"; + } else { + SDL_Log("No supported shader format found"); + return false; + } + info.num_samplers = 1; + info.num_uniform_buffers = 1; + info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; + shader = SDL_CreateGPUShader(device, &info); + if (!shader) { + SDL_Log("Couldn't create shader: %s", SDL_GetError()); + return false; + } + + SDL_INIT_INTERFACE(&desc); + desc.fragment_shader = shader; + render_state = SDL_CreateGPURenderState(renderer, &desc); + if (!render_state) { + SDL_Log("Couldn't create render state: %s", SDL_GetError()); + return false; + } + + SDL_zero(uniforms); + uniforms.distance_field_range = DISTANCE_FIELD_RANGE; + uniforms.texture_width = (float)font_texture->w; + uniforms.texture_height = (float)font_texture->h; + if (!SDL_SetGPURenderStateFragmentUniformData(render_state, 0, &uniforms, sizeof(uniforms))) { + SDL_Log("Couldn't set uniform data: %s", SDL_GetError()); + return false; + } + + return true; +} + +static void QuitGPURenderState(void) +{ + if (render_state) { + SDL_DestroyGPURenderState(render_state); + render_state = NULL; + } + if (shader) { + SDL_ReleaseGPUShader(device, shader); + shader = NULL; + } +} + +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + const char *description = "GPU render MSDF example"; + + SDL_SetAppMetadata(description, "1.0", "com.example.testgpurender_msdf"); + + if (!SDL_Init(SDL_INIT_VIDEO)) { + SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + window = SDL_CreateWindow(description, 640, 480, 0); + if (!window) { + SDL_Log("Couldn't create window: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + renderer = SDL_CreateRenderer(window, "gpu"); + if (!renderer) { + SDL_Log("Couldn't create renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + SDL_SetRenderVSync(renderer, 1); + + if (!LoadFontTexture() || !LoadFontLayout()) { + return SDL_APP_FAILURE; + } + + if (!InitGPURenderState()) { + return SDL_APP_FAILURE; + } + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + if (event->type == SDL_EVENT_QUIT || + (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE)) { + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + const char *text = "Hello world!"; + float text_width; + float text_height; + float x, y; + int output_width, output_height; + + SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + + text_height = 72.0f; + text_width = MeasureText(text, text_height); + SDL_GetCurrentRenderOutputSize(renderer, &output_width, &output_height); + x = (output_width - text_width) / 2; + y = (output_height - text_height) / 2; + SDL_SetRenderGPUState(renderer, render_state); + RenderText("Hello World!", text_height, x, y); + SDL_SetRenderGPUState(renderer, NULL); + + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + /* SDL will clean up the window/renderer for us. */ + QuitGPURenderState(); +} + diff --git a/test/testgpurender_msdf.frag.dxil.h b/test/testgpurender_msdf.frag.dxil.h new file mode 100644 index 0000000000..6a3037a702 --- /dev/null +++ b/test/testgpurender_msdf.frag.dxil.h @@ -0,0 +1,399 @@ +static const unsigned char testgpurender_msdf_frag_dxil[] = { + 0x44, 0x58, 0x42, 0x43, 0x2e, 0x49, 0xe7, 0xd0, 0x3a, 0xbb, 0xb2, 0xf2, + 0x7d, 0x23, 0x3a, 0x6b, 0x9a, 0xa6, 0x80, 0x0a, 0x01, 0x00, 0x00, 0x00, + 0x88, 0x12, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, + 0xf0, 0x01, 0x00, 0x00, 0xa0, 0x09, 0x00, 0x00, 0xbc, 0x09, 0x00, 0x00, + 0x53, 0x46, 0x49, 0x30, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x47, 0x31, 0x54, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x53, 0x47, 0x31, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x00, 0x00, + 0x50, 0x53, 0x56, 0x30, 0x04, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, + 0x44, 0x00, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x00, 0x6d, + 0x61, 0x69, 0x6e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x44, 0x00, 0x03, 0x02, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x42, 0x00, + 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x44, 0x10, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x53, 0x54, 0x41, 0x54, 0xa8, 0x07, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, 0xea, 0x01, 0x00, 0x00, 0x44, 0x58, 0x49, 0x4c, + 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x90, 0x07, 0x00, 0x00, + 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, 0xe1, 0x01, 0x00, 0x00, + 0x0b, 0x82, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, 0x06, 0x10, 0x32, 0x39, + 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, 0x1e, 0x04, 0x8b, 0x62, + 0x80, 0x18, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, 0xc4, 0x10, 0x32, 0x14, + 0x38, 0x08, 0x18, 0x4b, 0x0a, 0x32, 0x62, 0x88, 0x48, 0x90, 0x14, 0x20, + 0x43, 0x46, 0x88, 0xa5, 0x00, 0x19, 0x32, 0x42, 0xe4, 0x48, 0x0e, 0x90, + 0x11, 0x23, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, 0xe1, 0x83, 0xe5, 0x8a, + 0x04, 0x31, 0x46, 0x06, 0x51, 0x18, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x1b, 0x8c, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x40, 0x02, 0xa8, 0x0d, + 0x84, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x20, 0x6d, 0x30, 0x86, 0xff, + 0xff, 0xff, 0xff, 0x1f, 0x00, 0x09, 0xa8, 0x00, 0x49, 0x18, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x13, 0x82, 0x60, 0x42, 0x20, 0x4c, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x89, 0x20, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0x32, 0x22, 0x88, 0x09, 0x20, 0x64, 0x85, 0x04, 0x13, 0x23, 0xa4, 0x84, + 0x04, 0x13, 0x23, 0xe3, 0x84, 0xa1, 0x90, 0x14, 0x12, 0x4c, 0x8c, 0x8c, + 0x0b, 0x84, 0xc4, 0x4c, 0x10, 0x98, 0xc1, 0x08, 0x40, 0x09, 0x00, 0x0a, + 0x66, 0x00, 0xe6, 0x08, 0xc0, 0x60, 0x8e, 0x00, 0x29, 0xc6, 0x40, 0x10, + 0x44, 0x41, 0x90, 0x51, 0x0c, 0x80, 0x20, 0x88, 0x62, 0x20, 0xe4, 0xa6, + 0xe1, 0xf2, 0x27, 0xec, 0x21, 0x24, 0x7f, 0x25, 0xa4, 0x95, 0x98, 0xfc, + 0xe2, 0xb6, 0x51, 0x31, 0x0c, 0xc3, 0x40, 0x50, 0x71, 0xcf, 0x70, 0xf9, + 0x13, 0xf6, 0x10, 0x92, 0x1f, 0x02, 0xcd, 0xb0, 0x10, 0x28, 0x58, 0x0a, + 0xa3, 0x10, 0x0c, 0x33, 0x0c, 0xc3, 0x40, 0x10, 0xc4, 0x40, 0x4d, 0x21, + 0x06, 0x62, 0x18, 0xe8, 0x29, 0xc6, 0x40, 0x0c, 0xc3, 0x30, 0x50, 0x54, + 0x86, 0x81, 0x18, 0x68, 0x3a, 0x6a, 0xb8, 0xfc, 0x09, 0x7b, 0x08, 0xc9, + 0xe7, 0x36, 0xaa, 0x58, 0x89, 0xc9, 0x2f, 0x6e, 0x1b, 0x11, 0xc3, 0x30, + 0x0c, 0x85, 0xa8, 0x08, 0x86, 0x20, 0x6b, 0x8e, 0x20, 0x28, 0x06, 0x43, + 0x14, 0x04, 0x81, 0x51, 0x36, 0x10, 0x30, 0x8c, 0x40, 0x0c, 0x33, 0xb5, + 0xc1, 0x38, 0xb0, 0x43, 0x38, 0xcc, 0xc3, 0x3c, 0xb8, 0x01, 0x2d, 0x94, + 0x03, 0x3e, 0xd0, 0x43, 0x3d, 0xc8, 0x43, 0x39, 0xc8, 0x01, 0x29, 0xf0, + 0x81, 0x3d, 0x94, 0xc3, 0x38, 0xd0, 0xc3, 0x3b, 0xc8, 0x03, 0x1f, 0x98, + 0x03, 0x3b, 0xbc, 0x43, 0x38, 0xd0, 0x03, 0x1b, 0x80, 0x01, 0x1d, 0xf8, + 0x01, 0x18, 0xf8, 0x81, 0x1e, 0xe8, 0x41, 0x3b, 0xa4, 0x03, 0x3c, 0xcc, + 0xc3, 0x2f, 0xd0, 0x43, 0x3e, 0xc0, 0x43, 0x39, 0xa0, 0x80, 0x98, 0x49, + 0x0c, 0xc6, 0x81, 0x1d, 0xc2, 0x61, 0x1e, 0xe6, 0xc1, 0x0d, 0x68, 0xa1, + 0x1c, 0xf0, 0x81, 0x1e, 0xea, 0x41, 0x1e, 0xca, 0x41, 0x0e, 0x48, 0x81, + 0x0f, 0xec, 0xa1, 0x1c, 0xc6, 0x81, 0x1e, 0xde, 0x41, 0x1e, 0xf8, 0xc0, + 0x1c, 0xd8, 0xe1, 0x1d, 0xc2, 0x81, 0x1e, 0xd8, 0x00, 0x0c, 0xe8, 0xc0, + 0x0f, 0xc0, 0xc0, 0x0f, 0x90, 0xc0, 0x75, 0xe4, 0x0d, 0x23, 0x08, 0xc3, + 0x31, 0x13, 0xf6, 0x10, 0x3f, 0xe7, 0x34, 0x13, 0x71, 0x4d, 0x48, 0x18, + 0x40, 0x0a, 0x6f, 0x92, 0xa6, 0x88, 0x12, 0x26, 0x9f, 0x05, 0x98, 0x67, + 0x21, 0x22, 0x76, 0x02, 0x26, 0x02, 0x05, 0x04, 0x8d, 0x89, 0x40, 0x00, + 0x13, 0x14, 0x72, 0xc0, 0x87, 0x74, 0x60, 0x87, 0x36, 0x68, 0x87, 0x79, + 0x68, 0x03, 0x72, 0xc0, 0x87, 0x0d, 0xaf, 0x50, 0x0e, 0x6d, 0xd0, 0x0e, + 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, + 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0xa0, 0x07, 0x73, 0x20, 0x07, + 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, + 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe9, 0x30, 0x07, + 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x76, 0x40, 0x07, + 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xe6, 0x10, 0x07, 0x76, 0xa0, 0x07, + 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, 0x20, 0x07, 0x7a, 0x30, 0x07, + 0x72, 0xd0, 0x06, 0xe6, 0x60, 0x07, 0x74, 0xa0, 0x07, 0x76, 0x40, 0x07, + 0x6d, 0xe0, 0x0e, 0x78, 0xa0, 0x07, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, + 0x72, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x43, 0x9e, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x3c, 0x06, 0x10, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x79, 0x10, 0x20, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xf2, 0x34, + 0x40, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xe4, + 0x79, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0xc8, 0x13, 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x90, 0x67, 0x02, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x21, 0x8f, 0x05, 0x04, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x43, 0x9e, 0x0c, 0x08, 0x80, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb2, 0x40, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x32, 0x1e, 0x98, 0x18, 0x19, 0x11, 0x4c, 0x90, 0x8c, 0x09, 0x26, 0x47, + 0xc6, 0x04, 0x43, 0x22, 0x4a, 0x60, 0x04, 0xa0, 0x18, 0x8a, 0xa0, 0x24, + 0xca, 0xa0, 0x30, 0xca, 0xa1, 0x10, 0x4a, 0xa3, 0x3c, 0xca, 0xa5, 0xc4, + 0xa8, 0x28, 0x89, 0x11, 0x80, 0x22, 0x28, 0x84, 0x02, 0x21, 0xac, 0x06, + 0xe8, 0x9b, 0x01, 0xa0, 0x70, 0x06, 0x80, 0xc4, 0x19, 0x00, 0x22, 0x67, + 0x00, 0xa8, 0x1c, 0x0b, 0x31, 0x88, 0x40, 0x20, 0x10, 0x04, 0x01, 0x00, + 0x79, 0x18, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x1a, 0x03, 0x4c, 0x90, + 0x46, 0x02, 0x13, 0xc4, 0x8e, 0x0c, 0x6f, 0xec, 0xed, 0x4d, 0x0c, 0x24, + 0xc6, 0x05, 0xc7, 0x45, 0xa6, 0x06, 0x46, 0xc6, 0x05, 0x07, 0x04, 0x45, + 0x8c, 0xe6, 0x26, 0x26, 0x06, 0x67, 0x26, 0xa7, 0x2c, 0x65, 0x43, 0x10, + 0x4c, 0x10, 0x88, 0x64, 0x82, 0x40, 0x28, 0x1b, 0x84, 0x81, 0xd8, 0x20, + 0x10, 0x04, 0x05, 0xb8, 0xb9, 0x09, 0x02, 0xb1, 0x6c, 0x18, 0x0e, 0x84, + 0x98, 0x20, 0x7c, 0x1c, 0x93, 0xba, 0x2f, 0xba, 0x32, 0x3c, 0xba, 0x3a, + 0xb9, 0xb2, 0x09, 0x02, 0xc1, 0x4c, 0x10, 0x88, 0x66, 0x83, 0x40, 0x34, + 0x1b, 0x12, 0x42, 0x59, 0x18, 0x62, 0x60, 0x08, 0x67, 0x43, 0xf0, 0x4c, + 0x10, 0xc4, 0xc0, 0x23, 0x43, 0x97, 0x07, 0x57, 0xf6, 0x35, 0xf4, 0xe6, + 0x46, 0x57, 0x86, 0x47, 0x37, 0x41, 0x20, 0x9c, 0x09, 0x02, 0xf1, 0x6c, + 0x40, 0x88, 0x48, 0x9a, 0x88, 0x81, 0x02, 0x36, 0x04, 0xd5, 0x04, 0x81, + 0x0c, 0x3e, 0x26, 0x75, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, + 0x1b, 0x10, 0xe2, 0xc2, 0x18, 0x62, 0x20, 0x80, 0x0d, 0x41, 0xb6, 0x81, + 0x80, 0x00, 0x4b, 0x9b, 0x20, 0x84, 0x41, 0x47, 0x6e, 0xe8, 0xcd, 0x8d, + 0xae, 0x0c, 0x8f, 0xee, 0x8b, 0x2c, 0x6d, 0x8e, 0x2e, 0xcc, 0x6d, 0xac, + 0xec, 0xcb, 0x2c, 0xad, 0x8c, 0x8d, 0xec, 0x4b, 0x2e, 0xcc, 0xed, 0xac, + 0x6c, 0x82, 0x40, 0x40, 0x13, 0x04, 0x6c, 0xdb, 0x80, 0x20, 0xdd, 0x44, + 0x78, 0x4d, 0xf3, 0x91, 0x1a, 0x7a, 0x73, 0xa3, 0x2b, 0xc3, 0xa3, 0xfb, + 0xa2, 0x2b, 0xc3, 0xa3, 0xab, 0x93, 0x2b, 0xfb, 0x9a, 0x4b, 0xd3, 0x2b, + 0x9b, 0x20, 0x10, 0xd1, 0x04, 0x81, 0x90, 0x36, 0x28, 0x48, 0x18, 0x4c, + 0x62, 0xe0, 0x35, 0xcd, 0x37, 0x06, 0xcc, 0x86, 0x81, 0x02, 0x03, 0x32, + 0xd8, 0x30, 0x10, 0x5c, 0x19, 0x4c, 0x10, 0x04, 0x60, 0x03, 0xb0, 0x61, + 0x20, 0xd0, 0x00, 0x0d, 0x36, 0x04, 0x69, 0xb0, 0x61, 0x18, 0xce, 0x40, + 0x0d, 0x26, 0x08, 0x65, 0x00, 0x06, 0x1b, 0x02, 0x36, 0x20, 0xd1, 0x16, + 0x96, 0xe6, 0x46, 0x84, 0xaa, 0x08, 0x6b, 0xe8, 0xe9, 0x49, 0x8a, 0x68, + 0x82, 0x50, 0x58, 0x13, 0x84, 0xe2, 0xda, 0x10, 0x10, 0x13, 0x84, 0x02, + 0x9b, 0x20, 0x14, 0xd9, 0x04, 0x81, 0x98, 0x36, 0x08, 0x53, 0x1d, 0x6c, + 0x58, 0x88, 0x37, 0x80, 0x83, 0x38, 0x90, 0x83, 0x39, 0x18, 0xe8, 0x80, + 0x88, 0x03, 0x3b, 0xd8, 0x10, 0x0c, 0x1b, 0x84, 0x69, 0xda, 0xb0, 0x0c, + 0x6f, 0x00, 0x07, 0x71, 0x80, 0x07, 0x73, 0x30, 0xcc, 0xc1, 0x10, 0x07, + 0x79, 0xb0, 0x41, 0xb8, 0x03, 0x3d, 0x60, 0x32, 0x65, 0xf5, 0x45, 0x15, + 0x26, 0x77, 0x56, 0x46, 0x37, 0x41, 0x28, 0xb4, 0x0d, 0x0b, 0xc1, 0x07, + 0x70, 0xd0, 0x07, 0x72, 0x10, 0x07, 0x03, 0x1d, 0x10, 0x71, 0x60, 0x07, + 0x1b, 0x02, 0x3f, 0xd8, 0x30, 0xec, 0xc1, 0x1f, 0x00, 0x1b, 0x8a, 0x33, + 0x70, 0x03, 0x50, 0xd8, 0x00, 0x1a, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x73, + 0x13, 0x04, 0x82, 0x62, 0x91, 0xe6, 0x36, 0x47, 0x37, 0x37, 0x41, 0x20, + 0x2a, 0x1a, 0x73, 0x69, 0x67, 0x5f, 0x6c, 0x64, 0x34, 0xe6, 0xd2, 0xce, + 0xbe, 0xe6, 0xe8, 0x88, 0xd0, 0x95, 0xe1, 0x7d, 0xb9, 0xbd, 0xc9, 0xb5, + 0x6d, 0x50, 0x44, 0x61, 0x14, 0x48, 0xa1, 0x14, 0x4c, 0x01, 0x39, 0x05, + 0x31, 0x40, 0x85, 0xa1, 0x0a, 0x1b, 0x9b, 0x5d, 0x9b, 0x4b, 0x1a, 0x59, + 0x99, 0x1b, 0xdd, 0x94, 0x20, 0xa8, 0x42, 0x86, 0xe7, 0x62, 0x57, 0x26, + 0x37, 0x97, 0xf6, 0xe6, 0x36, 0x25, 0x20, 0x9a, 0x90, 0xe1, 0xb9, 0xd8, + 0x85, 0xb1, 0xd9, 0x95, 0xc9, 0x4d, 0x09, 0x8a, 0x3a, 0x64, 0x78, 0x2e, + 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x53, + 0x02, 0xa4, 0x0c, 0x19, 0x9e, 0x8b, 0x5c, 0xd9, 0xdc, 0x5b, 0x9d, 0xdc, + 0x58, 0xd9, 0xdc, 0x94, 0x40, 0xab, 0x44, 0x86, 0xe7, 0x42, 0x97, 0x07, + 0x57, 0x16, 0xe4, 0xe6, 0xf6, 0x46, 0x17, 0x46, 0x97, 0xf6, 0xe6, 0x36, + 0x37, 0x45, 0x28, 0x03, 0x35, 0xa8, 0x43, 0x86, 0xe7, 0x62, 0x97, 0x56, + 0x76, 0x97, 0x44, 0x36, 0x45, 0x17, 0x46, 0x57, 0x36, 0x25, 0x60, 0x83, + 0x3a, 0x64, 0x78, 0x2e, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x50, 0x6f, 0x69, + 0x6e, 0x74, 0x73, 0x53, 0x02, 0x50, 0xe8, 0x42, 0x86, 0xe7, 0x32, 0xf6, + 0x56, 0xe7, 0x46, 0x57, 0x26, 0x37, 0x37, 0x25, 0x40, 0x05, 0x00, 0x00, + 0x79, 0x18, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, + 0xc4, 0xe1, 0x1c, 0x66, 0x14, 0x01, 0x3d, 0x88, 0x43, 0x38, 0x84, 0xc3, + 0x8c, 0x42, 0x80, 0x07, 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, 0xe6, + 0x00, 0x0f, 0xed, 0x10, 0x0e, 0xf4, 0x80, 0x0e, 0x33, 0x0c, 0x42, 0x1e, + 0xc2, 0xc1, 0x1d, 0xce, 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, 0x88, 0x43, + 0x38, 0x84, 0x83, 0x1b, 0xcc, 0x03, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, + 0x3d, 0xcc, 0x78, 0x8c, 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, 0x48, + 0x87, 0x70, 0x70, 0x07, 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, + 0x87, 0x19, 0xcc, 0x11, 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, 0x6e, + 0x30, 0x0f, 0xe3, 0xf0, 0x0e, 0xf0, 0x50, 0x0e, 0x33, 0x10, 0xc4, 0x1d, + 0xde, 0x21, 0x1c, 0xd8, 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, 0x89, + 0x3b, 0xbc, 0x83, 0x3b, 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, + 0x3c, 0x84, 0x03, 0x3b, 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, 0x68, + 0x07, 0x37, 0x68, 0x87, 0x72, 0x68, 0x07, 0x37, 0x80, 0x87, 0x70, 0x90, + 0x87, 0x70, 0x60, 0x07, 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, 0x78, + 0x87, 0x77, 0x80, 0x87, 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, 0x98, + 0x87, 0x79, 0x98, 0x81, 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, + 0xc0, 0x0e, 0xec, 0x30, 0x03, 0x62, 0xc8, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, + 0xcc, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, 0x1c, + 0xc4, 0x81, 0x1d, 0xca, 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, 0xc8, 0x43, + 0x39, 0x98, 0x43, 0x39, 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, 0x43, + 0x38, 0x88, 0x03, 0x3b, 0x94, 0xc3, 0x2f, 0xbc, 0x83, 0x3c, 0xfc, 0x82, + 0x3b, 0xd4, 0x03, 0x3b, 0xb0, 0xc3, 0x8c, 0xc8, 0x21, 0x07, 0x7c, 0x70, + 0x03, 0x72, 0x10, 0x87, 0x73, 0x70, 0x03, 0x7b, 0x08, 0x07, 0x79, 0x60, + 0x87, 0x70, 0xc8, 0x87, 0x77, 0xa8, 0x07, 0x7a, 0x98, 0x81, 0x3c, 0xe4, + 0x80, 0x0f, 0x6e, 0x40, 0x0f, 0xe5, 0xd0, 0x0e, 0xf0, 0x00, 0x00, 0x00, + 0x71, 0x20, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x46, 0x00, 0x0d, 0x97, + 0xef, 0x3c, 0x7e, 0x80, 0x34, 0x40, 0x84, 0xf9, 0xc5, 0x6d, 0xdb, 0xc1, + 0x36, 0x5c, 0xbe, 0xf3, 0xf8, 0x42, 0x40, 0x15, 0x05, 0x11, 0x95, 0x0e, + 0x30, 0x94, 0x84, 0x01, 0x08, 0x98, 0x5f, 0xdc, 0xb6, 0x21, 0x48, 0xc3, + 0xe5, 0x3b, 0x8f, 0x2f, 0x44, 0x04, 0x30, 0x11, 0x21, 0xd0, 0x0c, 0x0b, + 0x61, 0x05, 0xce, 0x70, 0xf9, 0xce, 0xe3, 0x0f, 0xce, 0x64, 0xfb, 0xc5, + 0x6d, 0x5b, 0xc0, 0x34, 0x5c, 0xbe, 0xf3, 0xf8, 0x8b, 0x03, 0x0c, 0x62, + 0xf3, 0x50, 0x93, 0x5f, 0xdc, 0xb6, 0x0d, 0x40, 0xc3, 0xe5, 0x3b, 0x8f, + 0x2f, 0x01, 0xcc, 0xb3, 0x10, 0x7e, 0x71, 0xdb, 0x26, 0x50, 0x0d, 0x97, + 0xef, 0x3c, 0xbe, 0x34, 0x39, 0x11, 0x81, 0x52, 0xd3, 0x43, 0x4d, 0x7e, + 0x71, 0xdb, 0x66, 0xf0, 0x0c, 0x97, 0xef, 0x3c, 0x3e, 0xd5, 0x00, 0x11, + 0xe6, 0x17, 0xb7, 0x6d, 0x00, 0x04, 0x03, 0x20, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x41, 0x53, 0x48, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa0, 0x40, 0x54, 0xe9, 0xac, 0xdc, 0xa5, 0x12, + 0x84, 0x83, 0x18, 0xdf, 0x37, 0xc0, 0x99, 0x88, 0x44, 0x58, 0x49, 0x4c, + 0xc4, 0x08, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x31, 0x02, 0x00, 0x00, + 0x44, 0x58, 0x49, 0x4c, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0xac, 0x08, 0x00, 0x00, 0x42, 0x43, 0xc0, 0xde, 0x21, 0x0c, 0x00, 0x00, + 0x28, 0x02, 0x00, 0x00, 0x0b, 0x82, 0x20, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x07, 0x81, 0x23, 0x91, 0x41, 0xc8, 0x04, 0x49, + 0x06, 0x10, 0x32, 0x39, 0x92, 0x01, 0x84, 0x0c, 0x25, 0x05, 0x08, 0x19, + 0x1e, 0x04, 0x8b, 0x62, 0x80, 0x18, 0x45, 0x02, 0x42, 0x92, 0x0b, 0x42, + 0xc4, 0x10, 0x32, 0x14, 0x38, 0x08, 0x18, 0x4b, 0x0a, 0x32, 0x62, 0x88, + 0x48, 0x90, 0x14, 0x20, 0x43, 0x46, 0x88, 0xa5, 0x00, 0x19, 0x32, 0x42, + 0xe4, 0x48, 0x0e, 0x90, 0x11, 0x23, 0xc4, 0x50, 0x41, 0x51, 0x81, 0x8c, + 0xe1, 0x83, 0xe5, 0x8a, 0x04, 0x31, 0x46, 0x06, 0x51, 0x18, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x1b, 0x8c, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x07, + 0x40, 0x02, 0xa8, 0x0d, 0x84, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x03, 0x20, + 0x6d, 0x30, 0x86, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x09, 0xa8, 0x00, + 0x49, 0x18, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x82, 0x60, 0x42, + 0x20, 0x4c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0x89, 0x20, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0x32, 0x22, 0x88, 0x09, 0x20, 0x64, 0x85, 0x04, + 0x13, 0x23, 0xa4, 0x84, 0x04, 0x13, 0x23, 0xe3, 0x84, 0xa1, 0x90, 0x14, + 0x12, 0x4c, 0x8c, 0x8c, 0x0b, 0x84, 0xc4, 0x4c, 0x10, 0x98, 0xc1, 0x08, + 0x40, 0x09, 0x00, 0x0a, 0x66, 0x00, 0xe6, 0x08, 0xc0, 0x60, 0x8e, 0x00, + 0x29, 0xc6, 0x40, 0x10, 0x44, 0x41, 0x90, 0x51, 0x0c, 0x80, 0x20, 0x88, + 0x62, 0x20, 0xe4, 0xa6, 0xe1, 0xf2, 0x27, 0xec, 0x21, 0x24, 0x7f, 0x25, + 0xa4, 0x95, 0x98, 0xfc, 0xe2, 0xb6, 0x51, 0x31, 0x0c, 0xc3, 0x40, 0x50, + 0x71, 0xcf, 0x70, 0xf9, 0x13, 0xf6, 0x10, 0x92, 0x1f, 0x02, 0xcd, 0xb0, + 0x10, 0x28, 0x58, 0x0a, 0xa3, 0x10, 0x0c, 0x33, 0x0c, 0xc3, 0x40, 0x10, + 0xc4, 0x40, 0x4d, 0x21, 0x06, 0x62, 0x18, 0xe8, 0x29, 0xc6, 0x40, 0x0c, + 0xc3, 0x30, 0x50, 0x54, 0x86, 0x81, 0x18, 0x68, 0x3a, 0x6a, 0xb8, 0xfc, + 0x09, 0x7b, 0x08, 0xc9, 0xe7, 0x36, 0xaa, 0x58, 0x89, 0xc9, 0x2f, 0x6e, + 0x1b, 0x11, 0xc3, 0x30, 0x0c, 0x85, 0xa8, 0x08, 0x86, 0x20, 0x6b, 0x8e, + 0x20, 0x28, 0x06, 0x43, 0x14, 0x04, 0x81, 0x51, 0x36, 0x10, 0x30, 0x8c, + 0x40, 0x0c, 0x33, 0xb5, 0xc1, 0x38, 0xb0, 0x43, 0x38, 0xcc, 0xc3, 0x3c, + 0xb8, 0x01, 0x2d, 0x94, 0x03, 0x3e, 0xd0, 0x43, 0x3d, 0xc8, 0x43, 0x39, + 0xc8, 0x01, 0x29, 0xf0, 0x81, 0x3d, 0x94, 0xc3, 0x38, 0xd0, 0xc3, 0x3b, + 0xc8, 0x03, 0x1f, 0x98, 0x03, 0x3b, 0xbc, 0x43, 0x38, 0xd0, 0x03, 0x1b, + 0x80, 0x01, 0x1d, 0xf8, 0x01, 0x18, 0xf8, 0x81, 0x1e, 0xe8, 0x41, 0x3b, + 0xa4, 0x03, 0x3c, 0xcc, 0xc3, 0x2f, 0xd0, 0x43, 0x3e, 0xc0, 0x43, 0x39, + 0xa0, 0x80, 0x98, 0x49, 0x0c, 0xc6, 0x81, 0x1d, 0xc2, 0x61, 0x1e, 0xe6, + 0xc1, 0x0d, 0x68, 0xa1, 0x1c, 0xf0, 0x81, 0x1e, 0xea, 0x41, 0x1e, 0xca, + 0x41, 0x0e, 0x48, 0x81, 0x0f, 0xec, 0xa1, 0x1c, 0xc6, 0x81, 0x1e, 0xde, + 0x41, 0x1e, 0xf8, 0xc0, 0x1c, 0xd8, 0xe1, 0x1d, 0xc2, 0x81, 0x1e, 0xd8, + 0x00, 0x0c, 0xe8, 0xc0, 0x0f, 0xc0, 0xc0, 0x0f, 0x90, 0xc0, 0x75, 0xe4, + 0x0d, 0x23, 0x08, 0xc3, 0x31, 0x13, 0xf6, 0x10, 0x3f, 0xe7, 0x34, 0x13, + 0x71, 0x4d, 0x48, 0x18, 0x40, 0x0a, 0x6f, 0x92, 0xa6, 0x88, 0x12, 0x26, + 0x9f, 0x05, 0x98, 0x67, 0x21, 0x22, 0x76, 0x02, 0x26, 0x02, 0x05, 0x04, + 0x8d, 0x89, 0x40, 0x00, 0x13, 0x14, 0x72, 0xc0, 0x87, 0x74, 0x60, 0x87, + 0x36, 0x68, 0x87, 0x79, 0x68, 0x03, 0x72, 0xc0, 0x87, 0x0d, 0xaf, 0x50, + 0x0e, 0x6d, 0xd0, 0x0e, 0x7a, 0x50, 0x0e, 0x6d, 0x00, 0x0f, 0x7a, 0x30, + 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x71, 0xa0, + 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, 0x0e, 0x78, 0xa0, 0x07, 0x73, 0x20, + 0x07, 0x6d, 0x90, 0x0e, 0x71, 0x60, 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, + 0x06, 0xe9, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x90, + 0x0e, 0x76, 0x40, 0x07, 0x7a, 0x60, 0x07, 0x74, 0xd0, 0x06, 0xe6, 0x10, + 0x07, 0x76, 0xa0, 0x07, 0x73, 0x20, 0x07, 0x6d, 0x60, 0x0e, 0x73, 0x20, + 0x07, 0x7a, 0x30, 0x07, 0x72, 0xd0, 0x06, 0xe6, 0x60, 0x07, 0x74, 0xa0, + 0x07, 0x76, 0x40, 0x07, 0x6d, 0xe0, 0x0e, 0x78, 0xa0, 0x07, 0x71, 0x60, + 0x07, 0x7a, 0x30, 0x07, 0x72, 0xa0, 0x07, 0x76, 0x40, 0x07, 0x43, 0x9e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, + 0x3c, 0x06, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x79, 0x10, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0xf2, 0x34, 0x40, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0xe4, 0x79, 0x80, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0xc8, 0x13, 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x90, 0x67, 0x02, 0x02, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x21, 0x8f, 0x05, 0x04, 0xc0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x9e, 0x0c, 0x08, 0x80, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x40, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x32, 0x1e, 0x98, 0x14, 0x19, 0x11, 0x4c, 0x90, + 0x8c, 0x09, 0x26, 0x47, 0xc6, 0x04, 0x43, 0x22, 0x4a, 0x60, 0x04, 0xa0, + 0x20, 0x8a, 0xa1, 0x08, 0x4a, 0xa2, 0x0c, 0x0a, 0xa3, 0x3c, 0xa8, 0x28, + 0x89, 0x11, 0x80, 0x22, 0x28, 0x84, 0x02, 0xa1, 0x6f, 0x06, 0x80, 0xc4, + 0x19, 0x00, 0x22, 0x67, 0x00, 0xa8, 0x1c, 0x0b, 0x31, 0x88, 0x40, 0x20, + 0x10, 0x04, 0x01, 0x00, 0x79, 0x18, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, + 0x1a, 0x03, 0x4c, 0x90, 0x46, 0x02, 0x13, 0xc4, 0x8e, 0x0c, 0x6f, 0xec, + 0xed, 0x4d, 0x0c, 0x24, 0xc6, 0x05, 0xc7, 0x45, 0xa6, 0x06, 0x46, 0xc6, + 0x05, 0x07, 0x04, 0x45, 0x8c, 0xe6, 0x26, 0x26, 0x06, 0x67, 0x26, 0xa7, + 0x2c, 0x65, 0x43, 0x10, 0x4c, 0x10, 0x88, 0x64, 0x82, 0x40, 0x28, 0x1b, + 0x84, 0x81, 0x98, 0x20, 0x10, 0xcb, 0x06, 0x61, 0x30, 0x28, 0xc0, 0xcd, + 0x4d, 0x10, 0x08, 0x66, 0xc3, 0x80, 0x24, 0xc4, 0x04, 0xe1, 0xbb, 0x08, + 0x4c, 0x10, 0x88, 0x66, 0x82, 0x40, 0x38, 0x1b, 0x04, 0xc2, 0xd9, 0x90, + 0x10, 0x0b, 0xd3, 0x10, 0x43, 0x43, 0x3c, 0x1b, 0x02, 0x68, 0x82, 0x20, + 0x06, 0xd8, 0x04, 0x81, 0x78, 0x26, 0x08, 0x04, 0xb4, 0x01, 0x21, 0x24, + 0x66, 0x22, 0x06, 0x0a, 0xd8, 0x10, 0x54, 0x13, 0x04, 0x32, 0xc8, 0x36, + 0x20, 0xc4, 0xc5, 0x34, 0xc4, 0x40, 0x00, 0x1b, 0x02, 0x6c, 0x03, 0x11, + 0x01, 0x56, 0x36, 0x41, 0x28, 0x03, 0x6d, 0x43, 0xb0, 0x4d, 0x10, 0x04, + 0x80, 0x44, 0x5b, 0x58, 0x9a, 0x1b, 0x11, 0xaa, 0x22, 0xac, 0xa1, 0xa7, + 0x27, 0x29, 0xa2, 0x09, 0x42, 0x21, 0x4d, 0x10, 0x8a, 0x69, 0x43, 0x40, + 0x4c, 0x10, 0x0a, 0x6a, 0x82, 0x50, 0x54, 0x13, 0x04, 0x22, 0xda, 0x20, + 0x4c, 0x65, 0xb0, 0x61, 0x21, 0x3e, 0x30, 0x08, 0x03, 0x31, 0x18, 0x83, + 0x81, 0x0c, 0x88, 0x30, 0x30, 0x83, 0x0d, 0xc1, 0xb0, 0x41, 0x98, 0xa6, + 0x0d, 0xcb, 0xf0, 0x81, 0x41, 0x18, 0xa0, 0xc1, 0x18, 0x0c, 0x63, 0x30, + 0x84, 0x41, 0x1a, 0x6c, 0x10, 0xce, 0x40, 0x0d, 0x98, 0x4c, 0x59, 0x7d, + 0x51, 0x85, 0xc9, 0x9d, 0x95, 0xd1, 0x4d, 0x10, 0x0a, 0x6b, 0xc3, 0x42, + 0xb0, 0x01, 0x18, 0xb4, 0x81, 0x18, 0x84, 0xc1, 0x40, 0x06, 0x44, 0x18, + 0x98, 0xc1, 0x86, 0xc0, 0x0d, 0x36, 0x0c, 0x6b, 0xf0, 0x06, 0xc0, 0x86, + 0xa2, 0xf3, 0xe0, 0x40, 0x03, 0xaa, 0xb0, 0xb1, 0xd9, 0xb5, 0xb9, 0xa4, + 0x91, 0x95, 0xb9, 0xd1, 0x4d, 0x09, 0x82, 0x2a, 0x64, 0x78, 0x2e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x02, 0xa2, 0x09, 0x19, 0x9e, + 0x8b, 0x5d, 0x18, 0x9b, 0x5d, 0x99, 0xdc, 0x94, 0xc0, 0xa8, 0x43, 0x86, + 0xe7, 0x32, 0x87, 0x16, 0x46, 0x56, 0x26, 0xd7, 0xf4, 0x46, 0x56, 0xc6, + 0x36, 0x25, 0x48, 0xca, 0x90, 0xe1, 0xb9, 0xc8, 0x95, 0xcd, 0xbd, 0xd5, + 0xc9, 0x8d, 0x95, 0xcd, 0x4d, 0x09, 0xb2, 0x3a, 0x64, 0x78, 0x2e, 0x76, + 0x69, 0x65, 0x77, 0x49, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x82, + 0xad, 0x0e, 0x19, 0x9e, 0x4b, 0x99, 0x1b, 0x9d, 0x5c, 0x1e, 0xd4, 0x5b, + 0x9a, 0x1b, 0xdd, 0xdc, 0x94, 0x00, 0x0e, 0x00, 0x79, 0x18, 0x00, 0x00, + 0x4c, 0x00, 0x00, 0x00, 0x33, 0x08, 0x80, 0x1c, 0xc4, 0xe1, 0x1c, 0x66, + 0x14, 0x01, 0x3d, 0x88, 0x43, 0x38, 0x84, 0xc3, 0x8c, 0x42, 0x80, 0x07, + 0x79, 0x78, 0x07, 0x73, 0x98, 0x71, 0x0c, 0xe6, 0x00, 0x0f, 0xed, 0x10, + 0x0e, 0xf4, 0x80, 0x0e, 0x33, 0x0c, 0x42, 0x1e, 0xc2, 0xc1, 0x1d, 0xce, + 0xa1, 0x1c, 0x66, 0x30, 0x05, 0x3d, 0x88, 0x43, 0x38, 0x84, 0x83, 0x1b, + 0xcc, 0x03, 0x3d, 0xc8, 0x43, 0x3d, 0x8c, 0x03, 0x3d, 0xcc, 0x78, 0x8c, + 0x74, 0x70, 0x07, 0x7b, 0x08, 0x07, 0x79, 0x48, 0x87, 0x70, 0x70, 0x07, + 0x7a, 0x70, 0x03, 0x76, 0x78, 0x87, 0x70, 0x20, 0x87, 0x19, 0xcc, 0x11, + 0x0e, 0xec, 0x90, 0x0e, 0xe1, 0x30, 0x0f, 0x6e, 0x30, 0x0f, 0xe3, 0xf0, + 0x0e, 0xf0, 0x50, 0x0e, 0x33, 0x10, 0xc4, 0x1d, 0xde, 0x21, 0x1c, 0xd8, + 0x21, 0x1d, 0xc2, 0x61, 0x1e, 0x66, 0x30, 0x89, 0x3b, 0xbc, 0x83, 0x3b, + 0xd0, 0x43, 0x39, 0xb4, 0x03, 0x3c, 0xbc, 0x83, 0x3c, 0x84, 0x03, 0x3b, + 0xcc, 0xf0, 0x14, 0x76, 0x60, 0x07, 0x7b, 0x68, 0x07, 0x37, 0x68, 0x87, + 0x72, 0x68, 0x07, 0x37, 0x80, 0x87, 0x70, 0x90, 0x87, 0x70, 0x60, 0x07, + 0x76, 0x28, 0x07, 0x76, 0xf8, 0x05, 0x76, 0x78, 0x87, 0x77, 0x80, 0x87, + 0x5f, 0x08, 0x87, 0x71, 0x18, 0x87, 0x72, 0x98, 0x87, 0x79, 0x98, 0x81, + 0x2c, 0xee, 0xf0, 0x0e, 0xee, 0xe0, 0x0e, 0xf5, 0xc0, 0x0e, 0xec, 0x30, + 0x03, 0x62, 0xc8, 0xa1, 0x1c, 0xe4, 0xa1, 0x1c, 0xcc, 0xa1, 0x1c, 0xe4, + 0xa1, 0x1c, 0xdc, 0x61, 0x1c, 0xca, 0x21, 0x1c, 0xc4, 0x81, 0x1d, 0xca, + 0x61, 0x06, 0xd6, 0x90, 0x43, 0x39, 0xc8, 0x43, 0x39, 0x98, 0x43, 0x39, + 0xc8, 0x43, 0x39, 0xb8, 0xc3, 0x38, 0x94, 0x43, 0x38, 0x88, 0x03, 0x3b, + 0x94, 0xc3, 0x2f, 0xbc, 0x83, 0x3c, 0xfc, 0x82, 0x3b, 0xd4, 0x03, 0x3b, + 0xb0, 0xc3, 0x8c, 0xc8, 0x21, 0x07, 0x7c, 0x70, 0x03, 0x72, 0x10, 0x87, + 0x73, 0x70, 0x03, 0x7b, 0x08, 0x07, 0x79, 0x60, 0x87, 0x70, 0xc8, 0x87, + 0x77, 0xa8, 0x07, 0x7a, 0x98, 0x81, 0x3c, 0xe4, 0x80, 0x0f, 0x6e, 0x40, + 0x0f, 0xe5, 0xd0, 0x0e, 0xf0, 0x00, 0x00, 0x00, 0x71, 0x20, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x46, 0x00, 0x0d, 0x97, 0xef, 0x3c, 0x7e, 0x80, + 0x34, 0x40, 0x84, 0xf9, 0xc5, 0x6d, 0xdb, 0xc1, 0x36, 0x5c, 0xbe, 0xf3, + 0xf8, 0x42, 0x40, 0x15, 0x05, 0x11, 0x95, 0x0e, 0x30, 0x94, 0x84, 0x01, + 0x08, 0x98, 0x5f, 0xdc, 0xb6, 0x21, 0x48, 0xc3, 0xe5, 0x3b, 0x8f, 0x2f, + 0x44, 0x04, 0x30, 0x11, 0x21, 0xd0, 0x0c, 0x0b, 0x61, 0x05, 0xce, 0x70, + 0xf9, 0xce, 0xe3, 0x0f, 0xce, 0x64, 0xfb, 0xc5, 0x6d, 0x5b, 0xc0, 0x34, + 0x5c, 0xbe, 0xf3, 0xf8, 0x8b, 0x03, 0x0c, 0x62, 0xf3, 0x50, 0x93, 0x5f, + 0xdc, 0xb6, 0x0d, 0x40, 0xc3, 0xe5, 0x3b, 0x8f, 0x2f, 0x01, 0xcc, 0xb3, + 0x10, 0x7e, 0x71, 0xdb, 0x26, 0x50, 0x0d, 0x97, 0xef, 0x3c, 0xbe, 0x34, + 0x39, 0x11, 0x81, 0x52, 0xd3, 0x43, 0x4d, 0x7e, 0x71, 0xdb, 0x66, 0xf0, + 0x0c, 0x97, 0xef, 0x3c, 0x3e, 0xd5, 0x00, 0x11, 0xe6, 0x17, 0xb7, 0x6d, + 0x00, 0x04, 0x03, 0x20, 0x0d, 0x00, 0x00, 0x00, 0x61, 0x20, 0x00, 0x00, + 0x86, 0x00, 0x00, 0x00, 0x13, 0x04, 0x41, 0x2c, 0x10, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x84, 0x8d, 0x00, 0x50, 0x51, 0x02, 0x65, 0x40, + 0x44, 0xb1, 0x95, 0xdd, 0x0c, 0x40, 0x21, 0x94, 0x51, 0x29, 0x94, 0x5c, + 0x21, 0x95, 0x69, 0x40, 0xa1, 0x06, 0x14, 0x1e, 0x0d, 0x23, 0x00, 0x63, + 0x04, 0x20, 0x08, 0x82, 0xf0, 0x2f, 0x8c, 0x11, 0x80, 0x20, 0x08, 0xc2, + 0xdf, 0x0c, 0xc0, 0x18, 0x01, 0x08, 0x82, 0x20, 0xfe, 0x01, 0x00, 0x00, + 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, 0x99, 0x81, 0xb2, 0x91, 0x01, + 0x19, 0x4c, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, 0x9d, 0xc1, 0x22, + 0x95, 0x41, 0x19, 0x50, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, 0x90, 0xa1, + 0x01, 0xc3, 0x99, 0x81, 0x19, 0x54, 0x23, 0x06, 0x09, 0x00, 0x82, 0x60, + 0x60, 0xc0, 0x01, 0x84, 0x06, 0x67, 0xe0, 0x45, 0x23, 0x06, 0x09, 0x00, + 0x82, 0x60, 0x60, 0xc4, 0x41, 0x94, 0x06, 0x68, 0x60, 0x49, 0x23, 0x06, + 0x09, 0x00, 0x82, 0x60, 0x60, 0xc8, 0x81, 0x94, 0x06, 0x69, 0x00, 0x06, + 0xd3, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x18, 0x73, 0x30, 0xa9, 0x81, + 0x1a, 0x60, 0xd4, 0x88, 0x41, 0x02, 0x80, 0x20, 0x18, 0x18, 0x74, 0x40, + 0xad, 0xc1, 0x1a, 0x84, 0x41, 0x35, 0x62, 0x90, 0x00, 0x20, 0x08, 0x06, + 0x46, 0x1d, 0x54, 0x6c, 0xc0, 0x06, 0x99, 0x35, 0x62, 0xf0, 0x00, 0x20, + 0x08, 0x06, 0x0d, 0x1d, 0x3c, 0x09, 0x62, 0x14, 0xcb, 0xd2, 0x06, 0x6d, + 0x70, 0x2d, 0xa3, 0x09, 0x01, 0x30, 0x9a, 0x20, 0x04, 0xa3, 0x09, 0x83, + 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x53, 0x1d, 0x54, 0xca, 0x88, + 0x81, 0x01, 0x80, 0x20, 0x18, 0x4c, 0x76, 0x60, 0x29, 0x23, 0x06, 0x06, + 0x00, 0x82, 0x60, 0x30, 0xdd, 0x41, 0x1c, 0x08, 0x23, 0x06, 0x06, 0x00, + 0x82, 0x60, 0x30, 0xe1, 0x81, 0x1c, 0x08, 0x23, 0x06, 0x06, 0x00, 0x82, + 0x60, 0x30, 0xe5, 0x01, 0xe6, 0x8c, 0x18, 0x18, 0x00, 0x08, 0x82, 0xc1, + 0xa4, 0x07, 0x99, 0x33, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0xd3, 0x1e, + 0xd4, 0x81, 0x30, 0x62, 0x60, 0x00, 0x20, 0x08, 0x06, 0x13, 0x1f, 0xd8, + 0x81, 0x60, 0x82, 0x01, 0x1f, 0x13, 0x0c, 0xf8, 0x18, 0x26, 0xd0, 0xc7, + 0x32, 0x81, 0x3e, 0x23, 0x06, 0x07, 0x00, 0x82, 0x60, 0x60, 0x81, 0x02, + 0x1a, 0x5c, 0x7d, 0x30, 0x9a, 0x10, 0x00, 0xa3, 0x09, 0x42, 0x30, 0x9a, + 0x30, 0x08, 0x36, 0x08, 0xf4, 0x31, 0x42, 0xa0, 0xcf, 0x88, 0x41, 0x02, + 0x80, 0x20, 0x18, 0x44, 0xa8, 0xf0, 0x06, 0x42, 0x80, 0x1c, 0x16, 0x8c, + 0x81, 0x7c, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0xe0, 0x59, 0x85, 0x36, + 0x08, 0xc4, 0x60, 0xc4, 0xe0, 0x00, 0x40, 0x10, 0x0c, 0x1e, 0x56, 0x70, + 0x03, 0xec, 0x1a, 0x31, 0x38, 0x00, 0x10, 0x04, 0x83, 0xa7, 0x15, 0xd8, + 0x20, 0xb8, 0x46, 0x0c, 0x0e, 0x00, 0x04, 0xc1, 0xe0, 0x71, 0x85, 0x36, + 0xd0, 0xb2, 0x11, 0x83, 0x03, 0x00, 0x41, 0x30, 0x78, 0x5e, 0x21, 0x0e, + 0x02, 0xc1, 0x02, 0x35, 0x80, 0x8f, 0x05, 0x86, 0x7c, 0x2c, 0x58, 0x03, + 0xf8, 0x8c, 0x18, 0x1c, 0x00, 0x08, 0x82, 0xc1, 0x33, 0x0b, 0x75, 0x10, + 0xb8, 0xc1, 0x88, 0xc1, 0x01, 0x80, 0x20, 0x18, 0x3c, 0xb4, 0x30, 0x07, + 0xc1, 0x1a, 0x58, 0x30, 0x06, 0xf2, 0x19, 0x31, 0x48, 0x00, 0x10, 0x04, + 0x03, 0x04, 0x17, 0xee, 0x00, 0x16, 0x60, 0xe1, 0x14, 0xce, 0x60, 0xc4, + 0x20, 0x01, 0x40, 0x10, 0x0c, 0x10, 0x5c, 0xb8, 0x03, 0x58, 0x80, 0x05, + 0x3f, 0x30, 0x83, 0x11, 0x83, 0x04, 0x00, 0x41, 0x30, 0x40, 0x70, 0xe1, + 0x0e, 0x60, 0x01, 0x16, 0x4c, 0xa1, 0x0c, 0x46, 0x0c, 0x12, 0x00, 0x04, + 0xc1, 0x00, 0xc1, 0x85, 0x3b, 0x80, 0x05, 0x58, 0xe8, 0x83, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00 +}; +static const unsigned int testgpurender_msdf_frag_dxil_len = 4744; diff --git a/test/testgpurender_msdf.frag.hlsl b/test/testgpurender_msdf.frag.hlsl new file mode 100644 index 0000000000..2d852fbfcc --- /dev/null +++ b/test/testgpurender_msdf.frag.hlsl @@ -0,0 +1,37 @@ +cbuffer Context : register(b0, space3) { + float distance_field_range; + float2 texture_size; +}; + +Texture2D u_texture : register(t0, space2); +SamplerState u_sampler : register(s0, space2); + +struct PSInput { + float4 v_color : COLOR0; + float2 v_uv : TEXCOORD0; +}; + +struct PSOutput { + float4 o_color : SV_Target; +}; + +float median(float r, float g, float b) { + return max(min(r, g), min(max(r, g), b)); +} + +float screenPxRange(float2 texCoord) { + float2 unitRange = float2(distance_field_range, distance_field_range)/texture_size; + float2 screenTexSize = float2(1.0, 1.0)/fwidth(texCoord); + return max(0.5*dot(unitRange, screenTexSize), 1.0); +} + +PSOutput main(PSInput input) { + PSOutput output; + float3 msd = u_texture.Sample(u_sampler, input.v_uv).rgb; + float sd = median(msd.r, msd.g, msd.b); + float screenPxDistance = screenPxRange(input.v_uv)*(sd - 0.5); + float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0); + output.o_color.rgb = input.v_color.rgb; + output.o_color.a = (input.v_color.a * opacity); + return output; +} diff --git a/test/testgpurender_msdf.frag.msl.h b/test/testgpurender_msdf.frag.msl.h new file mode 100644 index 0000000000..6fc854f66a --- /dev/null +++ b/test/testgpurender_msdf.frag.msl.h @@ -0,0 +1,97 @@ +static const unsigned char testgpurender_msdf_frag_msl[] = { + 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x6d, 0x65, + 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x64, 0x6c, 0x69, 0x62, 0x3e, 0x0a, + 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x73, 0x69, + 0x6d, 0x64, 0x2f, 0x73, 0x69, 0x6d, 0x64, 0x2e, 0x68, 0x3e, 0x0a, 0x0a, + 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x3b, 0x0a, 0x0a, + 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x69, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x72, + 0x61, 0x6e, 0x67, 0x65, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, + 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x3b, 0x0a, 0x7d, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x33, + 0x35, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, + 0x74, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, + 0x74, 0x34, 0x20, 0x6f, 0x75, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x53, + 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x20, 0x5b, 0x5b, 0x63, + 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, 0x7d, + 0x3b, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x6d, 0x61, + 0x69, 0x6e, 0x30, 0x5f, 0x69, 0x6e, 0x0a, 0x7b, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x69, 0x6e, 0x5f, 0x76, + 0x61, 0x72, 0x5f, 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x30, 0x20, 0x5b, 0x5b, + 0x75, 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, 0x30, 0x29, 0x5d, + 0x5d, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x32, 0x20, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x54, 0x45, 0x58, + 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x30, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, + 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, 0x31, 0x29, 0x5d, 0x5d, 0x3b, 0x0a, + 0x7d, 0x3b, 0x0a, 0x0a, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6d, + 0x61, 0x69, 0x6e, 0x30, 0x28, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x69, + 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, + 0x5f, 0x69, 0x6e, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x74, 0x20, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x26, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x30, + 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, + 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x75, 0x5f, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x20, 0x5b, 0x5b, 0x74, 0x65, + 0x78, 0x74, 0x75, 0x72, 0x65, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x2c, 0x20, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x75, 0x5f, 0x73, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x5b, 0x5b, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x29, 0x0a, 0x7b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, + 0x74, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, + 0x34, 0x32, 0x20, 0x3d, 0x20, 0x75, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, + 0x72, 0x65, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x75, 0x5f, + 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x2c, 0x20, 0x69, 0x6e, 0x2e, + 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x54, 0x45, 0x58, 0x43, 0x4f, + 0x4f, 0x52, 0x44, 0x30, 0x29, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, 0x33, 0x20, 0x3d, 0x20, 0x5f, + 0x34, 0x32, 0x2e, 0x78, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x34, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x34, + 0x32, 0x2e, 0x79, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, + 0x61, 0x74, 0x34, 0x20, 0x5f, 0x36, 0x35, 0x20, 0x3d, 0x20, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x34, 0x28, 0x69, 0x6e, 0x2e, 0x69, 0x6e, 0x5f, 0x76, + 0x61, 0x72, 0x5f, 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x30, 0x2e, 0x78, 0x2c, + 0x20, 0x69, 0x6e, 0x2e, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x43, + 0x4f, 0x4c, 0x4f, 0x52, 0x30, 0x2e, 0x79, 0x2c, 0x20, 0x69, 0x6e, 0x2e, + 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x43, 0x4f, 0x4c, 0x4f, 0x52, + 0x30, 0x2e, 0x7a, 0x2c, 0x20, 0x5f, 0x33, 0x35, 0x2e, 0x77, 0x29, 0x3b, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x36, 0x35, 0x2e, 0x77, 0x20, 0x3d, + 0x20, 0x69, 0x6e, 0x2e, 0x69, 0x6e, 0x5f, 0x76, 0x61, 0x72, 0x5f, 0x43, + 0x4f, 0x4c, 0x4f, 0x52, 0x30, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x66, 0x61, + 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, 0x70, + 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, + 0x30, 0x2e, 0x35, 0x20, 0x2a, 0x20, 0x64, 0x6f, 0x74, 0x28, 0x66, 0x6c, + 0x6f, 0x61, 0x74, 0x32, 0x28, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x2e, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x29, 0x20, 0x2f, + 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, + 0x73, 0x69, 0x7a, 0x65, 0x29, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, + 0x32, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2f, 0x20, 0x66, 0x77, 0x69, + 0x64, 0x74, 0x68, 0x28, 0x69, 0x6e, 0x2e, 0x69, 0x6e, 0x5f, 0x76, 0x61, + 0x72, 0x5f, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x30, 0x29, + 0x29, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x70, + 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, + 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, + 0x28, 0x5f, 0x34, 0x33, 0x2c, 0x20, 0x5f, 0x34, 0x34, 0x29, 0x2c, 0x20, + 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, + 0x28, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x65, 0x3a, 0x3a, 0x6d, 0x61, + 0x78, 0x28, 0x5f, 0x34, 0x33, 0x2c, 0x20, 0x5f, 0x34, 0x34, 0x29, 0x2c, + 0x20, 0x5f, 0x34, 0x32, 0x2e, 0x7a, 0x29, 0x29, 0x20, 0x2d, 0x20, 0x30, + 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, + 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x5f, 0x76, + 0x61, 0x72, 0x5f, 0x53, 0x56, 0x5f, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x20, 0x3d, 0x20, 0x5f, 0x36, 0x35, 0x3b, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6f, 0x75, 0x74, 0x3b, 0x0a, + 0x7d, 0x0a, 0x0a +}; +static const unsigned int testgpurender_msdf_frag_msl_len = 1119; diff --git a/test/testgpurender_msdf.frag.spv.h b/test/testgpurender_msdf.frag.spv.h new file mode 100644 index 0000000000..1df67336fb --- /dev/null +++ b/test/testgpurender_msdf.frag.spv.h @@ -0,0 +1,162 @@ +static const unsigned char testgpurender_msdf_frag_spv[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x5f, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x74, 0x79, 0x70, 0x65, 0x2e, 0x32, 0x64, 0x2e, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x75, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, + 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x74, 0x79, 0x70, 0x65, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x75, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x2e, 0x76, + 0x61, 0x72, 0x2e, 0x43, 0x4f, 0x4c, 0x4f, 0x52, 0x30, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x2e, 0x76, + 0x61, 0x72, 0x2e, 0x54, 0x45, 0x58, 0x43, 0x4f, 0x4f, 0x52, 0x44, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x61, 0x72, 0x2e, 0x53, 0x56, 0x5f, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x07, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x74, 0x79, 0x70, 0x65, + 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x64, 0x2e, 0x69, 0x6d, 0x61, + 0x67, 0x65, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, + 0x2b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x2b, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x1b, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x25, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x56, 0x00, 0x05, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x57, 0x00, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x31, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, + 0x15, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x35, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0xd1, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x39, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x94, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, + 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x09, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x06, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x03, 0x00, 0x05, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 +}; +static const unsigned int testgpurender_msdf_frag_spv_len = 1904;