#include "pipeline.hpp" #include "../webgpu/gpu.hpp" #include "gx_fmt.hpp" #include "shader_info.hpp" #include "tracy/Tracy.hpp" #include #include #include #include namespace aurora::gx { static Module Log("aurora::gx"); namespace { struct ShaderConfigHash { size_t operator()(const ShaderConfig& config) const noexcept { return static_cast(xxh3_hash(config)); } }; struct CachedShaderModule { std::condition_variable ready; wgpu::ShaderModule module; bool compiling = true; }; std::mutex sShaderModuleCacheMutex; absl::flat_hash_map, ShaderConfigHash> sShaderModuleCache; wgpu::ShaderModule cached_shader_module(const ShaderConfig& config) { std::shared_ptr entry; { std::unique_lock lock{sShaderModuleCacheMutex}; const auto it = sShaderModuleCache.find(config); if (it == sShaderModuleCache.end()) { entry = std::make_shared(); sShaderModuleCache.emplace(config, entry); } else { entry = it->second; entry->ready.wait(lock, [&] { return !entry->compiling; }); return entry->module; } } auto module = build_shader(config); { std::lock_guard lock{sShaderModuleCacheMutex}; entry->module = module; entry->compiling = false; } entry->ready.notify_all(); return module; } } // namespace wgpu::RenderPipeline create_pipeline(const PipelineConfig& config) { ZoneScoped; const auto shader = cached_shader_module(config.shaderConfig); return build_pipeline(config, {}, shader, "GX Pipeline"); } void clear_shader_module_cache() { std::lock_guard lock{sShaderModuleCacheMutex}; sShaderModuleCache.clear(); } void render(const DrawData& data, const wgpu::RenderPassEncoder& pass, DrawEncodeState& state, bool requireReadyPipeline, const gfx::Range* uniformRangeOverride) { if (!gfx::bind_pipeline(data.pipeline, pass, state.currentPipeline, requireReadyPipeline)) { return; } // An interpolated presentation slot re-encodes the identical draw with only this range replaced; overriding here avoids copying the whole DrawData per draw per slot. const gfx::Range& uniformRange = uniformRangeOverride != nullptr ? *uniformRangeOverride : data.uniformRange; const std::array offsets{uniformRange.offset}; pass.SetBindGroup(1, gfx::g_uniformBindGroup, offsets.size(), offsets.data()); // Resolved when the draw was recorded; see GXBindGroups. if (data.bindGroups.resolvedTextureBindGroup != nullptr && data.bindGroups.resolvedTextureBindGroup != state.boundTextureBindGroup) { wgpuRenderPassEncoderSetBindGroup(pass.Get(), 2, data.bindGroups.resolvedTextureBindGroup, 0, nullptr); state.boundTextureBindGroup = data.bindGroups.resolvedTextureBindGroup; } if (data.dstAlpha != UINT32_MAX) { const wgpu::Color color{0.f, 0.f, 0.f, data.dstAlpha / 255.f}; pass.SetBlendConstant(&color); } if (!state.indexBufferBound) { // Bound once for the pass; draws select their range with firstIndex below. pass.SetIndexBuffer(gfx::g_indexBuffer, wgpu::IndexFormat::Uint16, 0, wgpu::kWholeSize); state.indexBufferBound = true; } pass.DrawIndexed(data.indexCount, data.instanceCount, static_cast(data.idxRange.offset / sizeof(uint16_t))); } } // namespace aurora::gx