RmlUi_Renderer_SDL_GPU.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019-2023 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "RmlUi_Renderer_SDL_GPU.h"
  29. #include "RmlUi_SDL_GPU/ShadersCompiledSPV.h"
  30. #include <RmlUi/Core/Core.h>
  31. #include <RmlUi/Core/FileInterface.h>
  32. #include <RmlUi/Core/Log.h>
  33. #include <RmlUi/Core/Types.h>
  34. #include <SDL3_image/SDL_image.h>
  35. using namespace Rml;
  36. enum ShaderType {
  37. ShaderTypeColor,
  38. ShaderTypeTexture,
  39. ShaderTypeVert,
  40. ShaderTypeCount,
  41. };
  42. enum ShaderFormat {
  43. ShaderFormatSPIRV,
  44. ShaderFormatMSL,
  45. ShaderFormatDXIL,
  46. ShaderFormatCount,
  47. };
  48. struct Shader {
  49. Span<const byte> data[ShaderFormatCount];
  50. int uniforms;
  51. int samplers;
  52. SDL_GPUShaderStage stage;
  53. };
  54. #undef X
  55. #define X(name) \
  56. Span<const byte> \
  57. { \
  58. name, sizeof(name) \
  59. }
  60. static const Shader shaders[ShaderTypeCount] = {
  61. {{X(shader_frag_color_spirv), X(shader_frag_color_msl), X(shader_frag_color_dxil)}, 0, 0, SDL_GPU_SHADERSTAGE_FRAGMENT},
  62. {{X(shader_frag_texture_spirv), X(shader_frag_texture_msl), X(shader_frag_texture_dxil)}, 0, 1, SDL_GPU_SHADERSTAGE_FRAGMENT},
  63. {{X(shader_vert_spirv), X(shader_vert_msl), X(shader_vert_dxil)}, 2, 0, SDL_GPU_SHADERSTAGE_VERTEX}};
  64. #undef X
  65. static SDL_GPUShader* CreateShaderFromMemory(SDL_GPUDevice* device, ShaderType type)
  66. {
  67. SDL_GPUShaderFormat sdl_shader_format = SDL_GetGPUShaderFormats(device);
  68. ShaderFormat format = ShaderFormatCount;
  69. const char* entrypoint = nullptr;
  70. if (sdl_shader_format & SDL_GPU_SHADERFORMAT_SPIRV)
  71. {
  72. sdl_shader_format = SDL_GPU_SHADERFORMAT_SPIRV;
  73. format = ShaderFormatSPIRV;
  74. entrypoint = "main";
  75. }
  76. else if (sdl_shader_format & SDL_GPU_SHADERFORMAT_DXIL)
  77. {
  78. sdl_shader_format = SDL_GPU_SHADERFORMAT_DXIL;
  79. format = ShaderFormatDXIL;
  80. entrypoint = "main";
  81. }
  82. else if (sdl_shader_format & SDL_GPU_SHADERFORMAT_MSL)
  83. {
  84. sdl_shader_format = SDL_GPU_SHADERFORMAT_MSL;
  85. format = ShaderFormatMSL;
  86. entrypoint = "main0";
  87. }
  88. else
  89. {
  90. RMLUI_ERRORMSG("Invalid shader format");
  91. return nullptr;
  92. }
  93. const Shader& shader = shaders[type];
  94. SDL_GPUShaderCreateInfo info{};
  95. info.code = static_cast<const Uint8*>(shader.data[format].data());
  96. info.code_size = shader.data[format].size();
  97. info.entrypoint = entrypoint;
  98. info.format = sdl_shader_format;
  99. info.stage = shader.stage;
  100. info.num_samplers = shader.samplers;
  101. info.num_uniform_buffers = shader.uniforms;
  102. SDL_GPUShader* sdl_shader = SDL_CreateGPUShader(device, &info);
  103. if (!sdl_shader)
  104. {
  105. Log::Message(Log::LT_ERROR, "Failed to create shader: %s", SDL_GetError());
  106. RMLUI_ERROR;
  107. }
  108. return sdl_shader;
  109. }
  110. void RenderInterface_SDL_GPU::CreatePipelines()
  111. {
  112. SDL_GPUShader* color_shader = CreateShaderFromMemory(device, ShaderTypeColor);
  113. SDL_GPUShader* texture_shader = CreateShaderFromMemory(device, ShaderTypeTexture);
  114. SDL_GPUShader* vert_shader = CreateShaderFromMemory(device, ShaderTypeVert);
  115. SDL_GPUColorTargetDescription target{};
  116. target.format = SDL_GetGPUSwapchainTextureFormat(device, window);
  117. target.blend_state.enable_blend = true;
  118. target.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
  119. target.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
  120. target.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
  121. target.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
  122. target.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
  123. target.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
  124. SDL_GPUVertexAttribute attrib[3]{};
  125. attrib[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
  126. attrib[0].location = 0;
  127. attrib[0].offset = offsetof(Vertex, position);
  128. attrib[1].format = SDL_GPU_VERTEXELEMENTFORMAT_UBYTE4_NORM;
  129. attrib[1].location = 1;
  130. attrib[1].offset = offsetof(Vertex, colour);
  131. attrib[2].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2;
  132. attrib[2].location = 2;
  133. attrib[2].offset = offsetof(Vertex, tex_coord);
  134. SDL_GPUVertexBufferDescription buffer{};
  135. buffer.pitch = sizeof(Vertex);
  136. SDL_GPUGraphicsPipelineCreateInfo info{};
  137. info.vertex_shader = vert_shader;
  138. info.target_info.num_color_targets = 1;
  139. info.target_info.color_target_descriptions = &target;
  140. info.vertex_input_state.num_vertex_attributes = 3;
  141. info.vertex_input_state.num_vertex_buffers = 1;
  142. info.vertex_input_state.vertex_attributes = attrib;
  143. info.vertex_input_state.vertex_buffer_descriptions = &buffer;
  144. info.fragment_shader = color_shader;
  145. color_pipeline = SDL_CreateGPUGraphicsPipeline(device, &info);
  146. if (!color_pipeline)
  147. {
  148. Log::Message(Log::LT_ERROR, "Failed to create color pipeline: %s", SDL_GetError());
  149. RMLUI_ERROR;
  150. }
  151. info.fragment_shader = texture_shader;
  152. texture_pipeline = SDL_CreateGPUGraphicsPipeline(device, &info);
  153. if (!texture_pipeline)
  154. {
  155. Log::Message(Log::LT_ERROR, "Failed to create texture pipeline: %s", SDL_GetError());
  156. RMLUI_ERROR;
  157. }
  158. SDL_ReleaseGPUShader(device, color_shader);
  159. SDL_ReleaseGPUShader(device, texture_shader);
  160. SDL_ReleaseGPUShader(device, vert_shader);
  161. }
  162. RenderInterface_SDL_GPU::RenderInterface_SDL_GPU(SDL_GPUDevice* device, SDL_Window* window) :
  163. device(device), window(window), copy_pass(nullptr), render_pass(nullptr)
  164. {
  165. CreatePipelines();
  166. SDL_GPUSamplerCreateInfo info{};
  167. info.min_filter = SDL_GPU_FILTER_LINEAR;
  168. info.mag_filter = SDL_GPU_FILTER_LINEAR;
  169. info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR;
  170. info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
  171. info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
  172. info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
  173. linear_sampler = SDL_CreateGPUSampler(device, &info);
  174. if (!linear_sampler)
  175. {
  176. Log::Message(Log::LT_ERROR, "Failed to acquire command buffer: %s", SDL_GetError());
  177. return;
  178. }
  179. }
  180. void RenderInterface_SDL_GPU::Shutdown()
  181. {
  182. for (Rml::UniquePtr<Command>& command : commands)
  183. {
  184. command->Update(*this);
  185. }
  186. for (Rml::UniquePtr<Buffer>& buffer : buffers)
  187. {
  188. SDL_ReleaseGPUTransferBuffer(device, buffer->transfer_buffer);
  189. SDL_ReleaseGPUBuffer(device, buffer->buffer);
  190. }
  191. SDL_ReleaseGPUSampler(device, linear_sampler);
  192. SDL_ReleaseGPUGraphicsPipeline(device, color_pipeline);
  193. SDL_ReleaseGPUGraphicsPipeline(device, texture_pipeline);
  194. }
  195. void RenderInterface_SDL_GPU::BeginFrame(SDL_GPUCommandBuffer* command_buffer, SDL_GPUTexture* swapchain_texture, uint32_t width, uint32_t height)
  196. {
  197. this->command_buffer = command_buffer;
  198. this->swapchain_texture = swapchain_texture;
  199. swapchain_width = width;
  200. swapchain_height = height;
  201. proj = Matrix4f::ProjectOrtho(0.0f, static_cast<float>(width), static_cast<float>(height), 0.0f, -10'000.f, 10'000.f);
  202. SetTransform(nullptr);
  203. EnableScissorRegion(false);
  204. }
  205. void RenderInterface_SDL_GPU::EndFrame()
  206. {
  207. for (Rml::UniquePtr<Command>& command : commands)
  208. {
  209. command->Update(*this);
  210. }
  211. commands.clear();
  212. if (copy_pass)
  213. {
  214. SDL_EndGPUCopyPass(copy_pass);
  215. copy_pass = nullptr;
  216. }
  217. if (render_pass)
  218. {
  219. SDL_EndGPURenderPass(render_pass);
  220. render_pass = nullptr;
  221. }
  222. }
  223. bool RenderInterface_SDL_GPU::BeginCopyPass()
  224. {
  225. if (copy_pass)
  226. {
  227. return true;
  228. }
  229. if (render_pass)
  230. {
  231. SDL_EndGPURenderPass(render_pass);
  232. render_pass = nullptr;
  233. }
  234. copy_pass = SDL_BeginGPUCopyPass(command_buffer);
  235. if (!copy_pass)
  236. {
  237. Log::Message(Log::LT_ERROR, "Failed to begin copy pass: %s", SDL_GetError());
  238. return false;
  239. }
  240. return true;
  241. }
  242. bool RenderInterface_SDL_GPU::BeginRenderPass()
  243. {
  244. if (render_pass)
  245. {
  246. return true;
  247. }
  248. if (copy_pass)
  249. {
  250. SDL_EndGPUCopyPass(copy_pass);
  251. copy_pass = nullptr;
  252. }
  253. SDL_GPUColorTargetInfo color_info{};
  254. color_info.texture = swapchain_texture;
  255. color_info.load_op = SDL_GPU_LOADOP_LOAD;
  256. color_info.store_op = SDL_GPU_STOREOP_STORE;
  257. render_pass = SDL_BeginGPURenderPass(command_buffer, &color_info, 1, nullptr);
  258. if (!render_pass)
  259. {
  260. Log::Message(Log::LT_ERROR, "Failed to begin render pass: %s", SDL_GetError());
  261. return false;
  262. }
  263. return true;
  264. }
  265. CompiledGeometryHandle RenderInterface_SDL_GPU::CompileGeometry(Span<const Vertex> vertices, Span<const int> indices)
  266. {
  267. if (!BeginCopyPass())
  268. {
  269. return 0;
  270. }
  271. uint32_t vertex_size = static_cast<int>(vertices.size() * sizeof(Vertex));
  272. uint32_t index_size = static_cast<int>(indices.size() * sizeof(int));
  273. GeometryView* geometry = new GeometryView();
  274. geometry->vertex_buffer = RequestBuffer(vertex_size, SDL_GPU_BUFFERUSAGE_VERTEX);
  275. geometry->index_buffer = RequestBuffer(index_size, SDL_GPU_BUFFERUSAGE_INDEX);
  276. if (!geometry->vertex_buffer || !geometry->index_buffer)
  277. {
  278. Log::Message(Log::LT_ERROR, "Failed to request buffer(s)");
  279. delete geometry;
  280. return 0;
  281. }
  282. void* vertex_data = SDL_MapGPUTransferBuffer(device, geometry->vertex_buffer->transfer_buffer, true);
  283. void* index_data = SDL_MapGPUTransferBuffer(device, geometry->index_buffer->transfer_buffer, true);
  284. if (!vertex_data || !index_data)
  285. {
  286. Log::Message(Log::LT_ERROR, "Failed to map transfer buffer(s): %s", SDL_GetError());
  287. delete geometry;
  288. return 0;
  289. }
  290. std::memcpy(vertex_data, vertices.data(), vertex_size);
  291. std::memcpy(index_data, indices.data(), index_size);
  292. SDL_UnmapGPUTransferBuffer(device, geometry->vertex_buffer->transfer_buffer);
  293. SDL_UnmapGPUTransferBuffer(device, geometry->index_buffer->transfer_buffer);
  294. SDL_GPUTransferBufferLocation location{};
  295. SDL_GPUBufferRegion region{};
  296. location.transfer_buffer = geometry->vertex_buffer->transfer_buffer;
  297. region.buffer = geometry->vertex_buffer->buffer;
  298. region.size = vertex_size;
  299. SDL_UploadToGPUBuffer(copy_pass, &location, &region, false);
  300. location.transfer_buffer = geometry->index_buffer->transfer_buffer;
  301. region.buffer = geometry->index_buffer->buffer;
  302. region.size = index_size;
  303. SDL_UploadToGPUBuffer(copy_pass, &location, &region, false);
  304. geometry->num_indices = static_cast<int>(indices.size());
  305. geometry->vertex_buffer->in_use = true;
  306. geometry->index_buffer->in_use = true;
  307. return reinterpret_cast<Rml::CompiledGeometryHandle>(geometry);
  308. }
  309. void RenderInterface_SDL_GPU::ReleaseGeometryCommand::Update(RenderInterface_SDL_GPU& interface)
  310. {
  311. (void)interface;
  312. GeometryView* geometry = reinterpret_cast<GeometryView*>(handle);
  313. geometry->vertex_buffer->in_use = false;
  314. geometry->index_buffer->in_use = false;
  315. delete geometry;
  316. }
  317. void RenderInterface_SDL_GPU::ReleaseGeometry(CompiledGeometryHandle handle)
  318. {
  319. commands.push_back(Rml::MakeUnique<ReleaseGeometryCommand>(handle));
  320. }
  321. void RenderInterface_SDL_GPU::RenderGeometryCommand::Update(RenderInterface_SDL_GPU& interface)
  322. {
  323. if (!interface.BeginRenderPass())
  324. {
  325. return;
  326. }
  327. GeometryView* geometry = reinterpret_cast<GeometryView*>(handle);
  328. if (texture != 0)
  329. {
  330. SDL_BindGPUGraphicsPipeline(interface.render_pass, interface.texture_pipeline);
  331. SDL_GPUTextureSamplerBinding texture_binding{};
  332. texture_binding.texture = reinterpret_cast<SDL_GPUTexture*>(texture);
  333. texture_binding.sampler = interface.linear_sampler;
  334. SDL_BindGPUFragmentSamplers(interface.render_pass, 0, &texture_binding, 1);
  335. }
  336. else
  337. {
  338. SDL_BindGPUGraphicsPipeline(interface.render_pass, interface.color_pipeline);
  339. }
  340. SDL_GPUBufferBinding vertex_buffer_binding{};
  341. SDL_GPUBufferBinding index_buffer_binding{};
  342. vertex_buffer_binding.buffer = geometry->vertex_buffer->buffer;
  343. index_buffer_binding.buffer = geometry->index_buffer->buffer;
  344. SDL_BindGPUVertexBuffers(interface.render_pass, 0, &vertex_buffer_binding, 1);
  345. SDL_BindGPUIndexBuffer(interface.render_pass, &index_buffer_binding, SDL_GPU_INDEXELEMENTSIZE_32BIT);
  346. SDL_SetGPUScissor(interface.render_pass, &interface.scissor);
  347. SDL_PushGPUVertexUniformData(interface.command_buffer, 0, &interface.transform, sizeof(interface.transform));
  348. SDL_PushGPUVertexUniformData(interface.command_buffer, 1, &translation, sizeof(translation));
  349. SDL_DrawGPUIndexedPrimitives(interface.render_pass, geometry->num_indices, 1, 0, 0, 0);
  350. }
  351. void RenderInterface_SDL_GPU::RenderGeometry(CompiledGeometryHandle handle, Vector2f translation, TextureHandle texture)
  352. {
  353. commands.push_back(Rml::MakeUnique<RenderGeometryCommand>(handle, translation, texture));
  354. }
  355. void RenderInterface_SDL_GPU::EnableScissorRegionCommand::Update(RenderInterface_SDL_GPU& interface)
  356. {
  357. if (!enable)
  358. {
  359. interface.scissor.x = 0;
  360. interface.scissor.y = 0;
  361. interface.scissor.w = interface.swapchain_width;
  362. interface.scissor.h = interface.swapchain_height;
  363. }
  364. }
  365. void RenderInterface_SDL_GPU::EnableScissorRegion(bool enable)
  366. {
  367. commands.push_back(Rml::MakeUnique<EnableScissorRegionCommand>(enable));
  368. }
  369. void RenderInterface_SDL_GPU::SetScissorRegionCommand::Update(RenderInterface_SDL_GPU& interface)
  370. {
  371. interface.scissor.x = region.Left();
  372. interface.scissor.w = region.Width();
  373. interface.scissor.y = region.Top();
  374. interface.scissor.h = region.Height();
  375. }
  376. void RenderInterface_SDL_GPU::SetScissorRegion(Rectanglei region)
  377. {
  378. commands.push_back(Rml::MakeUnique<SetScissorRegionCommand>(region));
  379. }
  380. TextureHandle RenderInterface_SDL_GPU::LoadTexture(Vector2i& texture_dimensions, const String& source)
  381. {
  382. Rml::FileInterface* file_interface = Rml::GetFileInterface();
  383. Rml::FileHandle file_handle = file_interface->Open(source);
  384. if (!file_handle)
  385. {
  386. return 0;
  387. }
  388. file_interface->Seek(file_handle, 0, SEEK_END);
  389. size_t buffer_size = file_interface->Tell(file_handle);
  390. file_interface->Seek(file_handle, 0, SEEK_SET);
  391. using Rml::byte;
  392. Rml::UniquePtr<byte[]> buffer(new byte[buffer_size]);
  393. file_interface->Read(buffer.get(), buffer_size, file_handle);
  394. file_interface->Close(file_handle);
  395. const size_t i_ext = source.rfind('.');
  396. Rml::String extension = (i_ext == Rml::String::npos ? Rml::String() : source.substr(i_ext + 1));
  397. SDL_Surface* surface = IMG_LoadTyped_IO(SDL_IOFromMem(buffer.get(), int(buffer_size)), 1, extension.c_str());
  398. if (!surface)
  399. {
  400. return 0;
  401. }
  402. texture_dimensions = {surface->w, surface->h};
  403. if (surface->format != SDL_PIXELFORMAT_RGBA32)
  404. {
  405. SDL_Surface* converted_surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
  406. SDL_DestroySurface(surface);
  407. if (!converted_surface)
  408. {
  409. return 0;
  410. }
  411. surface = converted_surface;
  412. }
  413. // Convert colors to premultiplied alpha, which is necessary for correct alpha compositing.
  414. const size_t pixels_byte_size = surface->w * surface->h * 4;
  415. byte* pixels = static_cast<byte*>(surface->pixels);
  416. for (size_t i = 0; i < pixels_byte_size; i += 4)
  417. {
  418. const byte alpha = pixels[i + 3];
  419. for (size_t j = 0; j < 3; ++j)
  420. {
  421. pixels[i + j] = byte(int(pixels[i + j]) * int(alpha) / 255);
  422. }
  423. }
  424. Span<const byte> data{static_cast<const byte*>(surface->pixels), static_cast<size_t>(surface->pitch * surface->h)};
  425. texture_dimensions = {surface->w, surface->h};
  426. TextureHandle handle = GenerateTexture(data, texture_dimensions);
  427. SDL_DestroySurface(surface);
  428. return handle;
  429. }
  430. TextureHandle RenderInterface_SDL_GPU::GenerateTexture(Span<const byte> source, Vector2i source_dimensions)
  431. {
  432. SDL_GPUTransferBuffer* transfer_buffer;
  433. {
  434. SDL_GPUTransferBufferCreateInfo info{};
  435. info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
  436. info.size = source_dimensions.x * source_dimensions.y * 4;
  437. transfer_buffer = SDL_CreateGPUTransferBuffer(device, &info);
  438. if (!transfer_buffer)
  439. {
  440. Log::Message(Log::LT_ERROR, "Failed to create transfer buffer: %s", SDL_GetError());
  441. return 0;
  442. }
  443. }
  444. void* dst = SDL_MapGPUTransferBuffer(device, transfer_buffer, false);
  445. if (!dst)
  446. {
  447. SDL_Log("Failed to map transfer buffer: %s", SDL_GetError());
  448. return 0;
  449. }
  450. std::memcpy(dst, source.data(), source_dimensions.x * source_dimensions.y * 4);
  451. SDL_UnmapGPUTransferBuffer(device, transfer_buffer);
  452. SDL_GPUTexture* texture;
  453. {
  454. SDL_GPUTextureCreateInfo info{};
  455. info.type = SDL_GPU_TEXTURETYPE_2D;
  456. info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
  457. info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
  458. info.width = source_dimensions.x;
  459. info.height = source_dimensions.y;
  460. info.layer_count_or_depth = 1;
  461. info.num_levels = 1;
  462. texture = SDL_CreateGPUTexture(device, &info);
  463. if (!texture)
  464. {
  465. Log::Message(Log::LT_ERROR, "Failed to create texture: %s", SDL_GetError());
  466. return 0;
  467. }
  468. }
  469. SDL_GPUTextureTransferInfo transfer_info{};
  470. SDL_GPUTextureRegion region{};
  471. transfer_info.transfer_buffer = transfer_buffer;
  472. region.texture = texture;
  473. region.w = source_dimensions.x;
  474. region.h = source_dimensions.y;
  475. region.d = 1;
  476. // We can get calls out outside of Begin/End Frame so always acquire a command buffer here
  477. SDL_GPUCommandBuffer* command_buffer = SDL_AcquireGPUCommandBuffer(device);
  478. if (!command_buffer)
  479. {
  480. Log::Message(Log::LT_ERROR, "Failed to acquire command buffer: %s", SDL_GetError());
  481. SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
  482. SDL_ReleaseGPUTexture(device, texture);
  483. return 0;
  484. }
  485. SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(command_buffer);
  486. if (!copy_pass)
  487. {
  488. Log::Message(Log::LT_ERROR, "Failed to begin copy pass: %s", SDL_GetError());
  489. SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
  490. SDL_ReleaseGPUTexture(device, texture);
  491. SDL_CancelGPUCommandBuffer(command_buffer);
  492. return 0;
  493. }
  494. SDL_UploadToGPUTexture(copy_pass, &transfer_info, &region, false);
  495. SDL_ReleaseGPUTransferBuffer(device, transfer_buffer);
  496. SDL_EndGPUCopyPass(copy_pass);
  497. SDL_SubmitGPUCommandBuffer(command_buffer);
  498. return reinterpret_cast<TextureHandle>(texture);
  499. }
  500. void RenderInterface_SDL_GPU::ReleaseTextureCommand::Update(RenderInterface_SDL_GPU& interface)
  501. {
  502. SDL_GPUTexture* texture = reinterpret_cast<SDL_GPUTexture*>(handle);
  503. SDL_ReleaseGPUTexture(interface.device, texture);
  504. }
  505. void RenderInterface_SDL_GPU::ReleaseTexture(TextureHandle texture_handle)
  506. {
  507. commands.push_back(Rml::MakeUnique<ReleaseTextureCommand>(texture_handle));
  508. }
  509. RenderInterface_SDL_GPU::SetTransformCommand::SetTransformCommand(const Rml::Matrix4f* new_transform)
  510. {
  511. if (new_transform)
  512. {
  513. has_transform = true;
  514. transform = *new_transform;
  515. }
  516. else
  517. {
  518. has_transform = false;
  519. }
  520. }
  521. void RenderInterface_SDL_GPU::SetTransformCommand::Update(RenderInterface_SDL_GPU& interface)
  522. {
  523. if (has_transform)
  524. {
  525. interface.transform = interface.proj * transform;
  526. }
  527. else
  528. {
  529. interface.transform = interface.proj;
  530. }
  531. }
  532. void RenderInterface_SDL_GPU::SetTransform(const Rml::Matrix4f* new_transform)
  533. {
  534. commands.push_back(Rml::MakeUnique<SetTransformCommand>(new_transform));
  535. }
  536. RenderInterface_SDL_GPU::Buffer* RenderInterface_SDL_GPU::RequestBuffer(int capacity, SDL_GPUBufferUsageFlags usage)
  537. {
  538. auto it = std::lower_bound(buffers.begin(), buffers.end(), capacity,
  539. [](const Rml::UniquePtr<Buffer>& lhs, int capacity) { return lhs->capacity < capacity; });
  540. for (auto tmp_it = it; tmp_it != buffers.end(); ++tmp_it)
  541. {
  542. const auto& buffer = *tmp_it;
  543. if (!buffer->in_use && buffer->usage == usage)
  544. {
  545. // set in_use as false and expect the caller to set it to true themselves
  546. buffer->in_use = false;
  547. return buffer.get();
  548. }
  549. }
  550. Rml::UniquePtr<Buffer> buffer = Rml::MakeUnique<Buffer>();
  551. {
  552. SDL_GPUTransferBufferCreateInfo info{};
  553. info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
  554. info.size = capacity;
  555. buffer->transfer_buffer = SDL_CreateGPUTransferBuffer(device, &info);
  556. }
  557. {
  558. SDL_GPUBufferCreateInfo info{};
  559. info.usage = usage;
  560. info.size = capacity;
  561. buffer->buffer = SDL_CreateGPUBuffer(device, &info);
  562. }
  563. if (!buffer->transfer_buffer || !buffer->buffer)
  564. {
  565. Log::Message(Log::LT_ERROR, "Failed to create buffer(s): %s", SDL_GetError());
  566. SDL_ReleaseGPUTransferBuffer(device, buffer->transfer_buffer);
  567. SDL_ReleaseGPUBuffer(device, buffer->buffer);
  568. return {};
  569. }
  570. buffer->usage = usage;
  571. buffer->in_use = false;
  572. buffer->capacity = capacity;
  573. auto inserted_it = buffers.insert(it, std::move(buffer));
  574. return inserted_it->get();
  575. }