Goodbye, WebGPU
The web backend however, is being kept - for the future in case WebGPU gets added back or WebGL support is added
This commit is contained in:
parent
239ecc8d56
commit
80a74c15cd
15 changed files with 1 additions and 1070 deletions
|
@ -84,7 +84,6 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3 -O0")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3 -O0")
|
||||||
|
|
||||||
set(ENABLE_WEB TRUE)
|
set(ENABLE_WEB TRUE)
|
||||||
set(ENABLE_WEBGPU TRUE)
|
|
||||||
|
|
||||||
set(NEEDS_HOSTED_SHADER_COMPILER TRUE)
|
set(NEEDS_HOSTED_SHADER_COMPILER TRUE)
|
||||||
endif()
|
endif()
|
||||||
|
@ -103,10 +102,6 @@ if(ENABLE_DX12)
|
||||||
list(APPEND SHADER_LANGUAGES "hlsl")
|
list(APPEND SHADER_LANGUAGES "hlsl")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_WEBGPU)
|
|
||||||
list(APPEND SHADER_LANGUAGES "wgsl")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_METAL)
|
if(ENABLE_METAL)
|
||||||
list(APPEND SHADER_LANGUAGES "msl")
|
list(APPEND SHADER_LANGUAGES "msl")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -29,8 +29,4 @@ endif()
|
||||||
|
|
||||||
if(ENABLE_VULKAN)
|
if(ENABLE_VULKAN)
|
||||||
add_subdirectory(vulkan)
|
add_subdirectory(vulkan)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_WEBGPU)
|
|
||||||
add_subdirectory(webgpu)
|
|
||||||
endif()
|
endif()
|
|
@ -1,14 +0,0 @@
|
||||||
set(HEADERS
|
|
||||||
include/gfx_webgpu.hpp
|
|
||||||
src/gfx_webgpu_buffer.hpp
|
|
||||||
src/gfx_webgpu_pipeline.hpp
|
|
||||||
src/gfx_webgpu_texture.hpp
|
|
||||||
src/gfx_webgpu_framebuffer.hpp
|
|
||||||
src/gfx_webgpu_renderpass.hpp
|
|
||||||
src/gfx_webgpu_commandbuffer.hpp)
|
|
||||||
|
|
||||||
add_library(GFXWebGPU STATIC
|
|
||||||
src/gfx_webgpu.cpp
|
|
||||||
${HEADERS})
|
|
||||||
target_include_directories(GFXWebGPU PUBLIC include PRIVATE src)
|
|
||||||
target_link_libraries(GFXWebGPU PUBLIC GFX)
|
|
|
@ -1,66 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <emscripten/html5_webgpu.h>
|
|
||||||
|
|
||||||
#include "gfx.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUPipeline;
|
|
||||||
|
|
||||||
class GFXWebGPU : public GFX {
|
|
||||||
public:
|
|
||||||
bool initialize(const GFXCreateInfo& createInfo) override;
|
|
||||||
|
|
||||||
ShaderLanguage accepted_shader_language() override;
|
|
||||||
const char* get_name() override;
|
|
||||||
|
|
||||||
// buffer
|
|
||||||
GFXBuffer* create_buffer(void* data, GFXSize size, bool is_dynamic, GFXBufferUsage usage) override;
|
|
||||||
void copy_buffer(GFXBuffer* buffer, void* data, GFXSize offset, GFXSize size) override;
|
|
||||||
|
|
||||||
void* get_buffer_contents(GFXBuffer* buffer) override;
|
|
||||||
void release_buffer_contents(GFXBuffer* buffer, void* handle) override;
|
|
||||||
|
|
||||||
// texture
|
|
||||||
GFXTexture* create_texture(const GFXTextureCreateInfo& info) override;
|
|
||||||
void copy_texture(GFXTexture* texture, void* data, GFXSize size) override;
|
|
||||||
void copy_texture(GFXTexture* from, GFXTexture* to) override;
|
|
||||||
void copy_texture(GFXTexture* from, GFXBuffer* to) override;
|
|
||||||
|
|
||||||
// sampler
|
|
||||||
GFXSampler* create_sampler(const GFXSamplerCreateInfo& info) override;
|
|
||||||
|
|
||||||
// framebuffer
|
|
||||||
GFXFramebuffer* create_framebuffer(const GFXFramebufferCreateInfo& info) override;
|
|
||||||
|
|
||||||
// render pass
|
|
||||||
GFXRenderPass* create_render_pass(const GFXRenderPassCreateInfo& info) override;
|
|
||||||
|
|
||||||
// pipeline
|
|
||||||
GFXPipeline* create_graphics_pipeline(const GFXGraphicsPipelineCreateInfo& info) override;
|
|
||||||
GFXPipeline* create_compute_pipeline(const GFXComputePipelineCreateInfo& info) override;
|
|
||||||
|
|
||||||
GFXCommandBuffer* acquire_command_buffer(bool for_presentation_use) override;
|
|
||||||
|
|
||||||
void submit(GFXCommandBuffer* command_buffer, platform::window_ptr window) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
WGPUSwapChain create_swapchain();
|
|
||||||
WGPUShaderModule create_shader(const uint32_t* code, uint32_t size, std::string_view label);
|
|
||||||
|
|
||||||
uint64_t get_bind_group_hash(GFXWebGPUPipeline* pipeline);
|
|
||||||
void cache_bind_group_state(GFXWebGPUPipeline* pipeline, WGPUBindGroupLayout group_layout);
|
|
||||||
void reset_bind_state();
|
|
||||||
|
|
||||||
WGPUDevice device;
|
|
||||||
WGPUQueue queue;
|
|
||||||
WGPUSwapChain swapchain;
|
|
||||||
|
|
||||||
struct BoundShaderBuffer {
|
|
||||||
GFXBuffer* buffer = nullptr;
|
|
||||||
uint32_t size = 0, offset = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::array<BoundShaderBuffer, 25> boundShaderBuffers;
|
|
||||||
std::array<GFXTexture*, 25> boundTextures;
|
|
||||||
std::array<GFXSampler*, 25> boundSamplers;
|
|
||||||
};
|
|
|
@ -1,894 +0,0 @@
|
||||||
#include "gfx_webgpu.hpp"
|
|
||||||
|
|
||||||
#include "gfx_commandbuffer.hpp"
|
|
||||||
#include "gfx_webgpu_buffer.hpp"
|
|
||||||
#include "gfx_webgpu_commandbuffer.hpp"
|
|
||||||
#include "gfx_webgpu_framebuffer.hpp"
|
|
||||||
#include "gfx_webgpu_pipeline.hpp"
|
|
||||||
#include "gfx_webgpu_renderpass.hpp"
|
|
||||||
#include "gfx_webgpu_sampler.hpp"
|
|
||||||
#include "gfx_webgpu_texture.hpp"
|
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
WGPUTextureFormat toPixFormat(GFXPixelFormat format) {
|
|
||||||
switch (format) {
|
|
||||||
case GFXPixelFormat::R_32F:
|
|
||||||
return WGPUTextureFormat_R32Float;
|
|
||||||
|
|
||||||
case GFXPixelFormat::R_16F:
|
|
||||||
return WGPUTextureFormat_R16Float;
|
|
||||||
|
|
||||||
case GFXPixelFormat::RGBA_32F:
|
|
||||||
return WGPUTextureFormat_RGBA32Float;
|
|
||||||
|
|
||||||
case GFXPixelFormat::RGBA8_UNORM:
|
|
||||||
return WGPUTextureFormat_RGBA8Unorm;
|
|
||||||
|
|
||||||
case GFXPixelFormat::R8_UNORM:
|
|
||||||
return WGPUTextureFormat_R8Unorm;
|
|
||||||
|
|
||||||
case GFXPixelFormat::R8G8_UNORM:
|
|
||||||
return WGPUTextureFormat_RG8Unorm;
|
|
||||||
|
|
||||||
case GFXPixelFormat::R8G8_SFLOAT:
|
|
||||||
return WGPUTextureFormat_RG16Float;
|
|
||||||
|
|
||||||
case GFXPixelFormat::R8G8B8A8_UNORM:
|
|
||||||
return WGPUTextureFormat_RGBA8Unorm;
|
|
||||||
|
|
||||||
case GFXPixelFormat::R16G16B16A16_SFLOAT:
|
|
||||||
return WGPUTextureFormat_RGBA16Float;
|
|
||||||
|
|
||||||
case GFXPixelFormat::DEPTH_32F:
|
|
||||||
return WGPUTextureFormat_Depth32Float;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WGPUTextureFormat_Undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUVertexFormat toVertFormat(GFXVertexFormat format) {
|
|
||||||
switch (format) {
|
|
||||||
case GFXVertexFormat::FLOAT2:
|
|
||||||
return WGPUVertexFormat_Float32x2;
|
|
||||||
case GFXVertexFormat::FLOAT3:
|
|
||||||
return WGPUVertexFormat_Float32x3;
|
|
||||||
case GFXVertexFormat::FLOAT4:
|
|
||||||
return WGPUVertexFormat_Float32x4;
|
|
||||||
case GFXVertexFormat::INT:
|
|
||||||
return WGPUVertexFormat_Sint32;
|
|
||||||
case GFXVertexFormat::INT4:
|
|
||||||
return WGPUVertexFormat_Sint32x4;
|
|
||||||
case GFXVertexFormat::UNORM4:
|
|
||||||
return WGPUVertexFormat_Unorm8x4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WGPUVertexFormat_Undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUBlendFactor toFactor(GFXBlendFactor factor) {
|
|
||||||
switch (factor) {
|
|
||||||
case GFXBlendFactor::Zero:
|
|
||||||
return WGPUBlendFactor_Zero;
|
|
||||||
case GFXBlendFactor::One:
|
|
||||||
return WGPUBlendFactor_One;
|
|
||||||
case GFXBlendFactor::OneMinusSrcAlpha:
|
|
||||||
return WGPUBlendFactor_OneMinusSrcAlpha;
|
|
||||||
case GFXBlendFactor::OneMinusSrcColor:
|
|
||||||
return WGPUBlendFactor_OneMinusSrc;
|
|
||||||
case GFXBlendFactor::SrcAlpha:
|
|
||||||
return WGPUBlendFactor_SrcAlpha;
|
|
||||||
case GFXBlendFactor::DstAlpha:
|
|
||||||
return WGPUBlendFactor_DstAlpha;
|
|
||||||
case GFXBlendFactor::SrcColor:
|
|
||||||
return WGPUBlendFactor_Src;
|
|
||||||
case GFXBlendFactor::DstColor:
|
|
||||||
return WGPUBlendFactor_Dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WGPUBlendFactor_One;
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUAddressMode toSamplerMode(SamplingMode mode) {
|
|
||||||
switch (mode) {
|
|
||||||
case SamplingMode::Repeat:
|
|
||||||
return WGPUAddressMode_Repeat;
|
|
||||||
case SamplingMode::ClampToEdge:
|
|
||||||
return WGPUAddressMode_ClampToEdge;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WGPUAddressMode_Repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUFilterMode toFilter(GFXFilter filter) {
|
|
||||||
switch (filter) {
|
|
||||||
case GFXFilter::Nearest:
|
|
||||||
return WGPUFilterMode_Nearest;
|
|
||||||
case GFXFilter::Linear:
|
|
||||||
return WGPUFilterMode_Linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
return WGPUFilterMode_Linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUCompareFunction toCompareFunc(GFXCompareFunction func) {
|
|
||||||
switch (func) {
|
|
||||||
case GFXCompareFunction::Never:
|
|
||||||
return WGPUCompareFunction_Never;
|
|
||||||
case GFXCompareFunction::Less:
|
|
||||||
return WGPUCompareFunction_Less;
|
|
||||||
case GFXCompareFunction::Equal:
|
|
||||||
return WGPUCompareFunction_Equal;
|
|
||||||
case GFXCompareFunction::LessOrEqual:
|
|
||||||
return WGPUCompareFunction_LessEqual;
|
|
||||||
case GFXCompareFunction::Greater:
|
|
||||||
return WGPUCompareFunction_Greater;
|
|
||||||
case GFXCompareFunction::NotEqual:
|
|
||||||
return WGPUCompareFunction_NotEqual;
|
|
||||||
case GFXCompareFunction::GreaterOrEqual:
|
|
||||||
return WGPUCompareFunction_GreaterEqual;
|
|
||||||
case GFXCompareFunction::Always:
|
|
||||||
return WGPUCompareFunction_Always;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUShaderModule GFXWebGPU::create_shader(const uint32_t* code, const uint32_t size, std::string_view label) {
|
|
||||||
WGPUShaderModuleSPIRVDescriptor spirv = {};
|
|
||||||
spirv.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor;
|
|
||||||
spirv.codeSize = size / sizeof(uint32_t);
|
|
||||||
spirv.code = code;
|
|
||||||
|
|
||||||
WGPUShaderModuleDescriptor desc = {};
|
|
||||||
desc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&spirv);
|
|
||||||
desc.label = label.data();
|
|
||||||
|
|
||||||
return wgpuDeviceCreateShaderModule(device, &desc);
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUSwapChain GFXWebGPU::create_swapchain() {
|
|
||||||
WGPUSurfaceDescriptorFromCanvasHTMLSelector canvDesc = {};
|
|
||||||
canvDesc.chain.sType = WGPUSType_SurfaceDescriptorFromCanvasHTMLSelector;
|
|
||||||
canvDesc.selector = "canvas";
|
|
||||||
|
|
||||||
WGPUSurfaceDescriptor surfDesc = {};
|
|
||||||
surfDesc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&canvDesc);
|
|
||||||
|
|
||||||
WGPUSurface surface = wgpuInstanceCreateSurface(nullptr, &surfDesc);
|
|
||||||
|
|
||||||
WGPUSwapChainDescriptor swapDesc = {};
|
|
||||||
swapDesc.usage = WGPUTextureUsage_RenderAttachment;
|
|
||||||
swapDesc.format = WGPUTextureFormat_BGRA8Unorm;
|
|
||||||
swapDesc.width = 800; // TODO: configurable
|
|
||||||
swapDesc.height = 450;
|
|
||||||
swapDesc.presentMode = WGPUPresentMode_Fifo;
|
|
||||||
|
|
||||||
return wgpuDeviceCreateSwapChain(device, surface, &swapDesc);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GFXWebGPU::initialize(const GFXCreateInfo& createInfo) {
|
|
||||||
device = emscripten_webgpu_get_device();
|
|
||||||
if(device == nullptr) {
|
|
||||||
prism::log("Failed to get WebGPU device!");
|
|
||||||
}
|
|
||||||
|
|
||||||
queue = wgpuDeviceGetQueue(device);
|
|
||||||
swapchain = create_swapchain();
|
|
||||||
|
|
||||||
prism::log("Initialized WebGPU!");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderLanguage GFXWebGPU::accepted_shader_language() {
|
|
||||||
return ShaderLanguage::WGSL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* GFXWebGPU::get_name() {
|
|
||||||
return "WebGPU";
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXCommandBuffer* GFXWebGPU::acquire_command_buffer(bool for_presentation_use) {
|
|
||||||
return new GFXCommandBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXBuffer* GFXWebGPU::create_buffer(void* data, const GFXSize size, const bool is_dynamic, const GFXBufferUsage usage) {
|
|
||||||
auto buffer = new GFXWebGPUBuffer();
|
|
||||||
|
|
||||||
buffer->size = size;
|
|
||||||
|
|
||||||
WGPUBufferDescriptor desc = {};
|
|
||||||
desc.usage = WGPUBufferUsage_CopyDst;
|
|
||||||
desc.size = size;
|
|
||||||
desc.mappedAtCreation = true;
|
|
||||||
|
|
||||||
buffer->handle = wgpuDeviceCreateBuffer(device, &desc);
|
|
||||||
|
|
||||||
wgpuQueueWriteBuffer(queue, buffer->handle, 0, data, size);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::copy_buffer(GFXBuffer* buffer, void* data, const GFXSize offset, const GFXSize size) {
|
|
||||||
auto wgpu_buffer = (GFXWebGPUBuffer*)buffer;
|
|
||||||
|
|
||||||
wgpuQueueWriteBuffer(queue, wgpu_buffer->handle, offset, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* GFXWebGPU::get_buffer_contents(GFXBuffer* buffer) {
|
|
||||||
auto wgpu_buffer = (GFXWebGPUBuffer*)buffer;
|
|
||||||
|
|
||||||
return wgpuBufferGetMappedRange(wgpu_buffer->handle, 0, wgpu_buffer->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::release_buffer_contents(GFXBuffer* buffer, void* handle) {
|
|
||||||
auto wgpu_buffer = (GFXWebGPUBuffer*)buffer;
|
|
||||||
|
|
||||||
wgpuBufferUnmap(wgpu_buffer->handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXTexture* GFXWebGPU::create_texture(const GFXTextureCreateInfo& info) {
|
|
||||||
auto texture = new GFXWebGPUTexture();
|
|
||||||
|
|
||||||
WGPUTextureDescriptor descriptor = {};
|
|
||||||
descriptor.size.width = info.width;
|
|
||||||
descriptor.size.height = info.height;
|
|
||||||
descriptor.size.depthOrArrayLayers = info.array_length;
|
|
||||||
|
|
||||||
descriptor.dimension = WGPUTextureDimension_2D;
|
|
||||||
|
|
||||||
descriptor.format = WGPUTextureFormat_BGRA8Unorm;
|
|
||||||
|
|
||||||
descriptor.label = info.label.c_str();
|
|
||||||
|
|
||||||
descriptor.mipLevelCount = info.mip_count;
|
|
||||||
|
|
||||||
descriptor.usage = WGPUTextureUsage_None;
|
|
||||||
|
|
||||||
if((info.usage & GFXTextureUsage::Sampled) == GFXTextureUsage::Sampled)
|
|
||||||
descriptor.usage |= WGPUTextureUsage_TextureBinding;
|
|
||||||
|
|
||||||
if((info.usage & GFXTextureUsage::Sampled) == GFXTextureUsage::Attachment)
|
|
||||||
descriptor.usage |= WGPUTextureUsage_RenderAttachment;
|
|
||||||
|
|
||||||
if((info.usage & GFXTextureUsage::Sampled) == GFXTextureUsage::TransferDst)
|
|
||||||
descriptor.usage |= WGPUTextureUsage_CopyDst;
|
|
||||||
|
|
||||||
if((info.usage & GFXTextureUsage::Sampled) == GFXTextureUsage::TransferSrc)
|
|
||||||
descriptor.usage |= WGPUTextureUsage_CopySrc;
|
|
||||||
|
|
||||||
if((info.usage & GFXTextureUsage::Storage) == GFXTextureUsage::Storage)
|
|
||||||
descriptor.usage |= WGPUTextureUsage_StorageBinding;
|
|
||||||
|
|
||||||
texture->handle = wgpuDeviceCreateTexture(device, &descriptor);
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::copy_texture(GFXTexture* texture, void* data, GFXSize size) {
|
|
||||||
GFX::copy_texture(texture, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::copy_texture(GFXTexture* from, GFXTexture* to) {
|
|
||||||
GFX::copy_texture(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::copy_texture(GFXTexture* from, GFXBuffer* to) {
|
|
||||||
GFX::copy_texture(from, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXSampler* GFXWebGPU::create_sampler(const GFXSamplerCreateInfo& info) {
|
|
||||||
auto sampler = new GFXWebGPUSampler();
|
|
||||||
|
|
||||||
return sampler;
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXFramebuffer* GFXWebGPU::create_framebuffer(const GFXFramebufferCreateInfo& info) {
|
|
||||||
auto framebuffer = new GFXWebGPUFramebuffer();
|
|
||||||
|
|
||||||
return framebuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXRenderPass* GFXWebGPU::create_render_pass(const GFXRenderPassCreateInfo& info) {
|
|
||||||
auto render_pass = new GFXWebGPURenderPass();
|
|
||||||
|
|
||||||
return render_pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXPipeline* GFXWebGPU::create_graphics_pipeline(const GFXGraphicsPipelineCreateInfo& info) {
|
|
||||||
auto pipeline = new GFXWebGPUPipeline();
|
|
||||||
|
|
||||||
WGPURenderPipelineDescriptor descriptor = {};
|
|
||||||
descriptor.label = info.label.c_str();
|
|
||||||
|
|
||||||
const bool has_vertex_stage = !info.shaders.vertex_src.empty();
|
|
||||||
if (has_vertex_stage) {
|
|
||||||
const bool vertex_use_shader_source = !info.shaders.vertex_src.is_path();
|
|
||||||
|
|
||||||
if (vertex_use_shader_source) {
|
|
||||||
auto vertex_shader_vector = info.shaders.vertex_src.as_bytecode();
|
|
||||||
|
|
||||||
descriptor.vertex.module = create_shader(vertex_shader_vector.data(), vertex_shader_vector.size() * sizeof(uint32_t), std::string(info.label + " vertex stage").c_str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto vertex_shader = prism::open_file(prism::internal_domain / (info.shaders.vertex_src.as_path().string() + ".wgsl.spv"), true);
|
|
||||||
vertex_shader->read_all();
|
|
||||||
|
|
||||||
descriptor.vertex.module = create_shader(vertex_shader->cast_data<uint32_t>(), vertex_shader->size(), info.shaders.vertex_src.as_path().string().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUFragmentState fragment = {};
|
|
||||||
|
|
||||||
const bool has_fragment_stage = !info.shaders.fragment_src.empty();
|
|
||||||
if (has_fragment_stage) {
|
|
||||||
descriptor.fragment = &fragment;
|
|
||||||
|
|
||||||
const bool fragment_use_shader_source = !info.shaders.fragment_src.is_path();
|
|
||||||
|
|
||||||
if (fragment_use_shader_source) {
|
|
||||||
auto fragment_shader_vector = info.shaders.fragment_src.as_bytecode();
|
|
||||||
|
|
||||||
fragment.module = create_shader(fragment_shader_vector.data(), fragment_shader_vector.size() * sizeof(uint32_t), std::string(info.label + " fragment stage").c_str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto fragment_shader = prism::open_file(prism::internal_domain / (info.shaders.fragment_src.as_path().string() + ".wgsl.spv"), true);
|
|
||||||
fragment_shader->read_all();
|
|
||||||
|
|
||||||
fragment.module = create_shader(fragment_shader->cast_data<uint32_t>(), fragment_shader->size(), info.shaders.fragment_src.as_path().string().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prism::log("building pipeline {}", info.label);
|
|
||||||
prism::log("--------");
|
|
||||||
|
|
||||||
// alright, webgpu in their infinite wisdom does not allow arbitrary binding locations
|
|
||||||
// so, to be consistent with what vulkan, metal and virtually every other API allows,
|
|
||||||
// we will create dummy buffers with no attributes attached. congrats webgpu devs.
|
|
||||||
int dummy_buffer_count = 0;
|
|
||||||
for (auto& binding : info.vertex_input.inputs) {
|
|
||||||
dummy_buffer_count = std::max(binding.location, dummy_buffer_count);
|
|
||||||
}
|
|
||||||
dummy_buffer_count += 1;
|
|
||||||
|
|
||||||
std::vector<std::vector<WGPUVertexAttribute>> attributes;
|
|
||||||
attributes.resize(dummy_buffer_count);
|
|
||||||
|
|
||||||
prism::log("dummy buffer count: {}", dummy_buffer_count);
|
|
||||||
|
|
||||||
for (auto& attribute : info.vertex_input.attributes) {
|
|
||||||
WGPUVertexAttribute description;
|
|
||||||
description.shaderLocation = attribute.location;
|
|
||||||
description.format = toVertFormat(attribute.format);
|
|
||||||
description.offset = attribute.offset;
|
|
||||||
|
|
||||||
attributes[attribute.binding].push_back(description);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<WGPUVertexBufferLayout> inputs;
|
|
||||||
inputs.resize(dummy_buffer_count);
|
|
||||||
for (auto& binding : info.vertex_input.inputs) {
|
|
||||||
prism::log("binding loc {}", binding.location);
|
|
||||||
|
|
||||||
WGPUVertexBufferLayout b;
|
|
||||||
b.attributes = attributes[binding.location].data();
|
|
||||||
b.attributeCount = attributes[binding.location].size();
|
|
||||||
b.arrayStride = binding.stride;
|
|
||||||
b.stepMode = WGPUVertexStepMode_Vertex;
|
|
||||||
|
|
||||||
inputs[binding.location] = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.vertex.buffers = inputs.data();
|
|
||||||
descriptor.vertex.bufferCount = inputs.size();
|
|
||||||
|
|
||||||
switch (info.rasterization.culling_mode) {
|
|
||||||
case GFXCullingMode::Backface:
|
|
||||||
descriptor.primitive.cullMode = WGPUCullMode_Back;
|
|
||||||
break;
|
|
||||||
case GFXCullingMode::Frontface:
|
|
||||||
descriptor.primitive.cullMode = WGPUCullMode_Front;
|
|
||||||
break;
|
|
||||||
case GFXCullingMode::None:
|
|
||||||
descriptor.primitive.cullMode = WGPUCullMode_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (info.rasterization.winding_mode) {
|
|
||||||
case GFXWindingMode::Clockwise:
|
|
||||||
descriptor.primitive.frontFace = WGPUFrontFace_CW;
|
|
||||||
break;
|
|
||||||
case GFXWindingMode::CounterClockwise:
|
|
||||||
descriptor.primitive.frontFace = WGPUFrontFace_CCW;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptor.primitive.stripIndexFormat = WGPUIndexFormat_Uint16;
|
|
||||||
descriptor.primitive.topology = WGPUPrimitiveTopology_TriangleList;
|
|
||||||
|
|
||||||
// create bind group layout
|
|
||||||
std::vector<WGPUBindGroupLayoutEntry> group_entries = {};
|
|
||||||
for (auto& binding : info.shader_input.bindings) {
|
|
||||||
// ignore push constants
|
|
||||||
if (binding.type == GFXBindingType::PushConstant)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
WGPUBindGroupLayoutEntry entry = {};
|
|
||||||
entry.binding = binding.binding;
|
|
||||||
entry.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
|
|
||||||
|
|
||||||
switch (binding.type) {
|
|
||||||
case GFXBindingType::StorageBuffer:
|
|
||||||
{
|
|
||||||
entry.buffer.type = WGPUBufferBindingType_Uniform;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXBindingType::StorageImage:
|
|
||||||
{
|
|
||||||
entry.storageTexture.access = WGPUStorageTextureAccess_WriteOnly;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXBindingType::SampledImage:
|
|
||||||
{
|
|
||||||
entry.texture.sampleType = WGPUTextureSampleType_Force32;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXBindingType::Sampler: {
|
|
||||||
entry.sampler.type = WGPUSamplerBindingType_Comparison;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
group_entries.push_back(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUBindGroupLayoutDescriptor bind_group_layout_descriptor = {};
|
|
||||||
bind_group_layout_descriptor.entryCount = group_entries.size();
|
|
||||||
bind_group_layout_descriptor.entries = group_entries.data();
|
|
||||||
|
|
||||||
pipeline->bind_group_layout = wgpuDeviceCreateBindGroupLayout(device, &bind_group_layout_descriptor);
|
|
||||||
|
|
||||||
pipeline->render_handle = wgpuDeviceCreateRenderPipeline(device, &descriptor);
|
|
||||||
|
|
||||||
return pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
GFXPipeline* GFXWebGPU::create_compute_pipeline(const GFXComputePipelineCreateInfo& info) {
|
|
||||||
auto pipeline = new GFXWebGPUPipeline();
|
|
||||||
|
|
||||||
WGPUComputePipelineDescriptor descriptor = {};
|
|
||||||
descriptor.label = info.label.c_str();
|
|
||||||
|
|
||||||
{
|
|
||||||
const bool use_shader_source = !info.compute_src.is_path();
|
|
||||||
|
|
||||||
if (use_shader_source) {
|
|
||||||
auto compute_shader_vector = info.compute_src.as_bytecode();
|
|
||||||
|
|
||||||
descriptor.compute.module = create_shader(compute_shader_vector.data(), compute_shader_vector.size() * sizeof(uint32_t), info.label.c_str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto compute_shader = prism::open_file(prism::internal_domain / (info.compute_src.as_path().string() + ".wgsl.spv"), true);
|
|
||||||
compute_shader->read_all();
|
|
||||||
|
|
||||||
descriptor.compute.module = create_shader(compute_shader->cast_data<uint32_t>(), compute_shader->size(), info.compute_src.as_path().string().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeline->compute_handle = wgpuDeviceCreateComputePipeline(device, &descriptor);
|
|
||||||
|
|
||||||
return pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::submit(GFXCommandBuffer* command_buffer, const platform::window_ptr window) {
|
|
||||||
WGPUTextureView backBufView = wgpuSwapChainGetCurrentTextureView(swapchain);
|
|
||||||
|
|
||||||
WGPUCommandEncoder command_encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
|
|
||||||
|
|
||||||
GFXWebGPUPipeline* current_pipeline = nullptr;
|
|
||||||
WGPURenderPassEncoder render_encoder = nullptr;
|
|
||||||
WGPUComputePassEncoder compute_encoder = nullptr;
|
|
||||||
GFXWebGPURenderPass* current_render_pass = nullptr;
|
|
||||||
GFXWebGPUFramebuffer* current_framebuffer = nullptr;
|
|
||||||
WGPUColor current_clear_color = {};
|
|
||||||
Viewport current_viewport {}; // lol webgpu doesn't even have a viewport type??
|
|
||||||
|
|
||||||
enum class CurrentEncoder {
|
|
||||||
None,
|
|
||||||
Render,
|
|
||||||
Compute,
|
|
||||||
} current_encoder = CurrentEncoder::None;
|
|
||||||
|
|
||||||
const auto need_encoder = [&](CurrentEncoder encoder, bool needs_reset = false) {
|
|
||||||
if(encoder != current_encoder || needs_reset) {
|
|
||||||
if(render_encoder != nullptr)
|
|
||||||
wgpuRenderPassEncoderEndPass(render_encoder);
|
|
||||||
|
|
||||||
if(compute_encoder != nullptr)
|
|
||||||
wgpuComputePassEncoderEndPass(compute_encoder);
|
|
||||||
|
|
||||||
render_encoder = nullptr;
|
|
||||||
compute_encoder = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(current_encoder == encoder && !needs_reset)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch(encoder) {
|
|
||||||
case CurrentEncoder::None:
|
|
||||||
break;
|
|
||||||
case CurrentEncoder::Render:
|
|
||||||
{
|
|
||||||
WGPURenderPassDescriptor render_pass_descriptor = {};
|
|
||||||
|
|
||||||
std::vector<WGPURenderPassColorAttachment> color_attachments;
|
|
||||||
|
|
||||||
if(current_framebuffer != nullptr) {
|
|
||||||
unsigned int i = 0;
|
|
||||||
for(const auto& attachment : current_framebuffer->attachments) {
|
|
||||||
if(attachment->format == WGPUTextureFormat_Depth32Float) {
|
|
||||||
// TODO: lol what
|
|
||||||
auto depth_attachment = new WGPURenderPassDepthStencilAttachment();
|
|
||||||
depth_attachment->view = attachment->view;
|
|
||||||
depth_attachment->depthLoadOp = WGPULoadOp_Clear;
|
|
||||||
depth_attachment->depthStoreOp = WGPUStoreOp_Store;
|
|
||||||
|
|
||||||
render_pass_descriptor.depthStencilAttachment = depth_attachment;
|
|
||||||
} else {
|
|
||||||
WGPURenderPassColorAttachment color_attachment = {};
|
|
||||||
color_attachment.view = attachment->view;
|
|
||||||
color_attachment.loadOp = WGPULoadOp_Clear;
|
|
||||||
color_attachment.storeOp = WGPUStoreOp_Store;
|
|
||||||
color_attachment.clearColor = current_clear_color;
|
|
||||||
|
|
||||||
color_attachments.push_back(color_attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we are rendering to the screen
|
|
||||||
WGPURenderPassColorAttachment color_attachment = {};
|
|
||||||
color_attachment.view = backBufView;
|
|
||||||
color_attachment.loadOp = WGPULoadOp_Clear;
|
|
||||||
color_attachment.storeOp = WGPUStoreOp_Store;
|
|
||||||
color_attachment.clearColor = current_clear_color;
|
|
||||||
|
|
||||||
color_attachments.push_back(color_attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
render_pass_descriptor.colorAttachmentCount = color_attachments.size();
|
|
||||||
render_pass_descriptor.colorAttachments = color_attachments.data();
|
|
||||||
|
|
||||||
render_encoder = wgpuCommandEncoderBeginRenderPass(command_encoder, &render_pass_descriptor);
|
|
||||||
|
|
||||||
//if(currentViewport.width != 0.0f && currentViewport.height != 0.0f)
|
|
||||||
// renderEncoder->setViewport(currentViewport);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CurrentEncoder::Compute:
|
|
||||||
{
|
|
||||||
WGPUComputePassDescriptor compute_pass_descriptor = {};
|
|
||||||
|
|
||||||
compute_encoder = wgpuCommandEncoderBeginComputePass(command_encoder, &compute_pass_descriptor);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_encoder = encoder;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t last_bind_group_hash = 0;
|
|
||||||
|
|
||||||
const auto try_bind_group = [&]() -> bool {
|
|
||||||
if(current_pipeline == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(last_bind_group_hash != get_bind_group_hash(current_pipeline)) {
|
|
||||||
if(!current_pipeline->cached_bind_groups.count(get_bind_group_hash(current_pipeline)))
|
|
||||||
cache_bind_group_state(current_pipeline, current_pipeline->bind_group_layout);
|
|
||||||
|
|
||||||
auto& bind_group = current_pipeline->cached_bind_groups[get_bind_group_hash(current_pipeline)];
|
|
||||||
if(bind_group == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
wgpuRenderPassEncoderSetBindGroup(render_encoder, 0, bind_group, 0, nullptr);
|
|
||||||
|
|
||||||
last_bind_group_hash = get_bind_group_hash(current_pipeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
for(auto command : command_buffer->commands) {
|
|
||||||
switch (command.type) {
|
|
||||||
case GFXCommandType::Invalid:
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetRenderPass:
|
|
||||||
{
|
|
||||||
current_clear_color = {};
|
|
||||||
current_clear_color.r = command.data.set_render_pass.clear_color.r;
|
|
||||||
current_clear_color.g = command.data.set_render_pass.clear_color.g;
|
|
||||||
current_clear_color.b = command.data.set_render_pass.clear_color.b;
|
|
||||||
current_clear_color.a = command.data.set_render_pass.clear_color.a;
|
|
||||||
|
|
||||||
current_framebuffer = (GFXWebGPUFramebuffer*)command.data.set_render_pass.framebuffer;
|
|
||||||
current_render_pass = (GFXWebGPURenderPass*)command.data.set_render_pass.render_pass;
|
|
||||||
current_viewport = {};
|
|
||||||
|
|
||||||
need_encoder(CurrentEncoder::Render, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::EndRenderPass:
|
|
||||||
{
|
|
||||||
current_render_pass = nullptr;
|
|
||||||
|
|
||||||
if(current_encoder == CurrentEncoder::Render)
|
|
||||||
wgpuRenderPassEncoderEndPass(render_encoder);
|
|
||||||
}
|
|
||||||
case GFXCommandType::SetGraphicsPipeline:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Render);
|
|
||||||
|
|
||||||
current_pipeline = (GFXWebGPUPipeline*)command.data.set_graphics_pipeline.pipeline;
|
|
||||||
if(current_pipeline != nullptr) {
|
|
||||||
wgpuRenderPassEncoderSetPipeline(render_encoder, current_pipeline->render_handle);
|
|
||||||
|
|
||||||
reset_bind_state();
|
|
||||||
last_bind_group_hash = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetComputePipeline:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Compute);
|
|
||||||
|
|
||||||
current_pipeline = (GFXWebGPUPipeline*)command.data.set_compute_pipeline.pipeline;
|
|
||||||
if(current_pipeline != nullptr) {
|
|
||||||
wgpuComputePassEncoderSetPipeline(compute_encoder, current_pipeline->compute_handle);
|
|
||||||
|
|
||||||
reset_bind_state();
|
|
||||||
last_bind_group_hash = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetVertexBuffer:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Render);
|
|
||||||
|
|
||||||
wgpuRenderPassEncoderSetVertexBuffer(render_encoder,
|
|
||||||
command.data.set_vertex_buffer.index,
|
|
||||||
((GFXWebGPUBuffer*)command.data.set_vertex_buffer.buffer)->handle,
|
|
||||||
command.data.set_vertex_buffer.offset,
|
|
||||||
((GFXWebGPUBuffer*)command.data.set_vertex_buffer.buffer)->size);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetIndexBuffer:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Render);
|
|
||||||
|
|
||||||
wgpuRenderPassEncoderSetIndexBuffer(render_encoder,
|
|
||||||
((GFXWebGPUBuffer*)command.data.set_index_buffer.buffer)->handle,
|
|
||||||
command.data.set_index_buffer.index_type == IndexType::UINT32 ? WGPUIndexFormat_Uint32 : WGPUIndexFormat_Uint16,
|
|
||||||
0,
|
|
||||||
((GFXWebGPUBuffer*)command.data.set_index_buffer.buffer)->size);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::BindShaderBuffer:
|
|
||||||
{
|
|
||||||
BoundShaderBuffer bsb;
|
|
||||||
bsb.buffer = command.data.bind_shader_buffer.buffer;
|
|
||||||
bsb.offset = command.data.bind_shader_buffer.offset;
|
|
||||||
bsb.size = command.data.bind_shader_buffer.size;
|
|
||||||
|
|
||||||
boundShaderBuffers[command.data.bind_shader_buffer.index] = bsb;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::BindTexture:
|
|
||||||
{
|
|
||||||
boundTextures[command.data.bind_texture.index] = command.data.bind_texture.texture;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::BindSampler:
|
|
||||||
{
|
|
||||||
boundSamplers[command.data.bind_sampler.index] = command.data.bind_sampler.sampler;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::Draw:
|
|
||||||
{
|
|
||||||
if(current_pipeline == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(try_bind_group()) {
|
|
||||||
wgpuRenderPassEncoderDraw(render_encoder,
|
|
||||||
command.data.draw.vertex_count,
|
|
||||||
command.data.draw.instance_count,
|
|
||||||
command.data.draw.vertex_offset,
|
|
||||||
command.data.draw.base_instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::DrawIndexed:
|
|
||||||
{
|
|
||||||
if(current_pipeline == nullptr)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(try_bind_group()) {
|
|
||||||
wgpuRenderPassEncoderDrawIndexed(render_encoder,
|
|
||||||
command.data.draw_indexed.index_count,
|
|
||||||
1,
|
|
||||||
command.data.draw_indexed.vertex_offset,
|
|
||||||
command.data.draw_indexed.vertex_offset,
|
|
||||||
command.data.draw_indexed.base_instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::MemoryBarrier:
|
|
||||||
// not supported
|
|
||||||
break;
|
|
||||||
case GFXCommandType::CopyTexture:
|
|
||||||
{
|
|
||||||
// TODO: blit op
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetViewport:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Render);
|
|
||||||
|
|
||||||
current_viewport = command.data.set_viewport.viewport;
|
|
||||||
|
|
||||||
wgpuRenderPassEncoderSetViewport(render_encoder,
|
|
||||||
current_viewport.x,
|
|
||||||
current_viewport.y,
|
|
||||||
current_viewport.width,
|
|
||||||
current_viewport.height,
|
|
||||||
current_viewport.min_depth,
|
|
||||||
current_viewport.max_depth);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetScissor:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Render);
|
|
||||||
|
|
||||||
wgpuRenderPassEncoderSetScissorRect(render_encoder,
|
|
||||||
command.data.set_scissor.rect.offset.x,
|
|
||||||
command.data.set_scissor.rect.offset.y,
|
|
||||||
command.data.set_scissor.rect.extent.width,
|
|
||||||
command.data.set_scissor.rect.extent.height);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::GenerateMipmaps:
|
|
||||||
{
|
|
||||||
// TODO: not supported by webgpu?
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::SetDepthBias: {
|
|
||||||
need_encoder(CurrentEncoder::Render);
|
|
||||||
|
|
||||||
// TODO: not supported by webgpu?
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::PushGroup:
|
|
||||||
{
|
|
||||||
// TOOD: stub
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::PopGroup:
|
|
||||||
{
|
|
||||||
// TOOD: stub
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::InsertLabel:
|
|
||||||
{
|
|
||||||
// TOOD: stub
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GFXCommandType::Dispatch:
|
|
||||||
{
|
|
||||||
need_encoder(CurrentEncoder::Compute);
|
|
||||||
|
|
||||||
if(try_bind_group()) {
|
|
||||||
wgpuComputePassEncoderDispatch(compute_encoder,
|
|
||||||
command.data.dispatch.group_count_x,
|
|
||||||
command.data.dispatch.group_count_y,
|
|
||||||
command.data.dispatch.group_count_z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
prism::log("Unhandled GFX command {}", utility::enum_to_string(command.type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(render_encoder != nullptr)
|
|
||||||
wgpuRenderPassEncoderEndPass(render_encoder);
|
|
||||||
|
|
||||||
if(compute_encoder != nullptr)
|
|
||||||
wgpuComputePassEncoderEndPass(compute_encoder);
|
|
||||||
|
|
||||||
WGPUCommandBuffer commands = wgpuCommandEncoderFinish(command_encoder, nullptr);
|
|
||||||
|
|
||||||
wgpuQueueSubmit(queue, 1, &commands);
|
|
||||||
wgpuCommandBufferRelease(commands);
|
|
||||||
|
|
||||||
wgpuTextureViewRelease(backBufView);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t GFXWebGPU::get_bind_group_hash(GFXWebGPUPipeline *pipeline) {
|
|
||||||
uint64_t hash = 0;
|
|
||||||
hash += (int64_t)pipeline;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (auto& buffer : boundShaderBuffers) {
|
|
||||||
if (buffer.buffer != nullptr) {
|
|
||||||
hash += (uint64_t)buffer.buffer * (i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
for (auto& texture : boundTextures) {
|
|
||||||
if (texture != nullptr) {
|
|
||||||
hash += (uint64_t)texture * (i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::cache_bind_group_state(GFXWebGPUPipeline* pipeline, WGPUBindGroupLayout group_layout) {
|
|
||||||
uint64_t hash = get_bind_group_hash(pipeline);
|
|
||||||
|
|
||||||
std::vector<WGPUBindGroupEntry> group_entries;
|
|
||||||
|
|
||||||
for (auto [i, buffer] : utility::enumerate(boundShaderBuffers)) {
|
|
||||||
if (buffer.buffer != nullptr) {
|
|
||||||
auto wgpu_buffer = (GFXWebGPUBuffer*)buffer.buffer;
|
|
||||||
|
|
||||||
WGPUBindGroupEntry entry = {};
|
|
||||||
entry.buffer = wgpu_buffer->handle;
|
|
||||||
entry.size = buffer.size;
|
|
||||||
entry.offset = buffer.offset;
|
|
||||||
entry.binding = i;
|
|
||||||
|
|
||||||
group_entries.push_back(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto [i, texture] : utility::enumerate(boundTextures)) {
|
|
||||||
if (texture != nullptr) {
|
|
||||||
auto wgpu_texture = (GFXWebGPUTexture*) texture;
|
|
||||||
|
|
||||||
WGPUBindGroupEntry entry = {};
|
|
||||||
entry.textureView = wgpu_texture->view;
|
|
||||||
entry.sampler = wgpu_texture->sampler;
|
|
||||||
entry.binding = i;
|
|
||||||
|
|
||||||
group_entries.push_back(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto [i, sampler] : utility::enumerate(boundSamplers)) {
|
|
||||||
if (sampler != nullptr) {
|
|
||||||
auto wgpu_sampler = (GFXWebGPUSampler*) sampler;
|
|
||||||
|
|
||||||
WGPUBindGroupEntry entry = {};
|
|
||||||
entry.sampler = wgpu_sampler->handle;
|
|
||||||
entry.binding = i;
|
|
||||||
|
|
||||||
group_entries.push_back(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WGPUBindGroupDescriptor group_descriptor = {};
|
|
||||||
group_descriptor.layout = group_layout;
|
|
||||||
group_descriptor.entryCount = group_entries.size();
|
|
||||||
group_descriptor.entries = group_entries.data();
|
|
||||||
|
|
||||||
pipeline->cached_bind_groups[hash] = wgpuDeviceCreateBindGroup(device, &group_descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GFXWebGPU::reset_bind_state() {
|
|
||||||
for (auto& buffer : boundShaderBuffers)
|
|
||||||
buffer.buffer = nullptr;
|
|
||||||
|
|
||||||
for (auto& texture : boundTextures)
|
|
||||||
texture = nullptr;
|
|
||||||
|
|
||||||
for (auto& sampler : boundSamplers)
|
|
||||||
sampler = nullptr;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gfx_buffer.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUBuffer : public GFXBuffer {
|
|
||||||
public:
|
|
||||||
WGPUBuffer handle = nullptr;
|
|
||||||
size_t size = 0;
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gfx_commandbuffer.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUCommandBuffer : public GFXCommandBuffer {
|
|
||||||
public:
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gfx_framebuffer.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUTexture;
|
|
||||||
|
|
||||||
class GFXWebGPUFramebuffer : public GFXFramebuffer {
|
|
||||||
public:
|
|
||||||
std::vector<GFXWebGPUTexture*> attachments;
|
|
||||||
};
|
|
|
@ -1,15 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "gfx_pipeline.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUPipeline : public GFXPipeline {
|
|
||||||
public:
|
|
||||||
WGPURenderPipeline render_handle = nullptr;
|
|
||||||
WGPUComputePipeline compute_handle = nullptr;
|
|
||||||
|
|
||||||
WGPUBindGroupLayout bind_group_layout = nullptr;
|
|
||||||
|
|
||||||
std::map<uint64_t, WGPUBindGroup> cached_bind_groups;
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gfx_renderpass.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPURenderPass : public GFXRenderPass {
|
|
||||||
public:
|
|
||||||
|
|
||||||
};
|
|
|
@ -1,8 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gfx_sampler.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUSampler : public GFXSampler {
|
|
||||||
public:
|
|
||||||
WGPUSampler handle = nullptr;
|
|
||||||
};
|
|
|
@ -1,11 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "gfx_texture.hpp"
|
|
||||||
|
|
||||||
class GFXWebGPUTexture : public GFXTexture {
|
|
||||||
public:
|
|
||||||
WGPUTexture handle = nullptr;
|
|
||||||
WGPUTextureFormat format = WGPUTextureFormat_Undefined;
|
|
||||||
WGPUTextureView view = nullptr;
|
|
||||||
WGPUSampler sampler = nullptr;
|
|
||||||
};
|
|
|
@ -20,8 +20,7 @@ enum class ShaderLanguage {
|
||||||
GLSL,
|
GLSL,
|
||||||
SPIRV,
|
SPIRV,
|
||||||
MSL,
|
MSL,
|
||||||
HLSL,
|
HLSL
|
||||||
WGSL // lol how do we even convert to this
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Compilation options when compiling shaders.
|
/// Compilation options when compiling shaders.
|
||||||
|
@ -36,8 +35,6 @@ public:
|
||||||
|
|
||||||
/// When compiling MSL, the result may differ whether or not we're targetting non-Mac Metal platforms.
|
/// When compiling MSL, the result may differ whether or not we're targetting non-Mac Metal platforms.
|
||||||
bool is_apple_mobile = false;
|
bool is_apple_mobile = false;
|
||||||
|
|
||||||
bool enable_wgpu_compat = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents the source code of a shader either in plaintext (GLSL, MSL) or bytecode (SPIR-V).
|
/// Represents the source code of a shader either in plaintext (GLSL, MSL) or bytecode (SPIR-V).
|
||||||
|
|
|
@ -32,14 +32,6 @@ std::vector<uint32_t> compile_glsl_to_spv(const std::string_view source_string,
|
||||||
|
|
||||||
newString += source_string;
|
newString += source_string;
|
||||||
|
|
||||||
if(options.enable_wgpu_compat) {
|
|
||||||
// in some wacky world the webgpu devs live in, push constants do not exist.
|
|
||||||
|
|
||||||
// alright, so to make webgpu happy we are going to hand-waive all of our push constant blocks
|
|
||||||
// away into UBOs. let's start by rewriting our GLSL. yay...
|
|
||||||
newString = replace_substring(newString, "push_constant", "binding = 10");
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* InputCString = newString.c_str();
|
const char* InputCString = newString.c_str();
|
||||||
|
|
||||||
glslang::TShader shader(shader_language);
|
glslang::TShader shader(shader_language);
|
||||||
|
@ -104,8 +96,6 @@ std::optional<ShaderSource> ShaderCompiler::compile(const ShaderLanguage from_la
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.enable_wgpu_compat = to_language == ShaderLanguage::WGSL;
|
|
||||||
|
|
||||||
auto spirv = compile_glsl_to_spv(shader_source.as_string(), lang, options);
|
auto spirv = compile_glsl_to_spv(shader_source.as_string(), lang, options);
|
||||||
if(spirv.empty()) {
|
if(spirv.empty()) {
|
||||||
prism::log("SPIRV generation failed!");
|
prism::log("SPIRV generation failed!");
|
||||||
|
@ -115,7 +105,6 @@ std::optional<ShaderSource> ShaderCompiler::compile(const ShaderLanguage from_la
|
||||||
switch(to_language) {
|
switch(to_language) {
|
||||||
case ShaderLanguage::GLSL:
|
case ShaderLanguage::GLSL:
|
||||||
return ShaderSource(shader_source);
|
return ShaderSource(shader_source);
|
||||||
case ShaderLanguage::WGSL:
|
|
||||||
case ShaderLanguage::SPIRV:
|
case ShaderLanguage::SPIRV:
|
||||||
return ShaderSource(spirv);
|
return ShaderSource(spirv);
|
||||||
case ShaderLanguage::MSL: {
|
case ShaderLanguage::MSL: {
|
||||||
|
|
|
@ -26,8 +26,6 @@ int main(int argc, char* argv[]) {
|
||||||
language = ShaderLanguage::SPIRV;
|
language = ShaderLanguage::SPIRV;
|
||||||
} else if(shader_language_string == "msl") {
|
} else if(shader_language_string == "msl") {
|
||||||
language = ShaderLanguage::MSL;
|
language = ShaderLanguage::MSL;
|
||||||
} else if(shader_language_string == "wgsl") {
|
|
||||||
language = ShaderLanguage::WGSL;
|
|
||||||
} else if(shader_language_string == "glsl") {
|
} else if(shader_language_string == "glsl") {
|
||||||
language = ShaderLanguage::GLSL;
|
language = ShaderLanguage::GLSL;
|
||||||
} else if(shader_language_string == "hlsl") {
|
} else if(shader_language_string == "hlsl") {
|
||||||
|
@ -66,7 +64,6 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
switch(language) {
|
switch(language) {
|
||||||
// right now, WGSL is outputted as SPIR-V with some WGSL compatibility stuff included
|
// right now, WGSL is outputted as SPIR-V with some WGSL compatibility stuff included
|
||||||
case ShaderLanguage::WGSL:
|
|
||||||
case ShaderLanguage::SPIRV:
|
case ShaderLanguage::SPIRV:
|
||||||
{
|
{
|
||||||
const auto spirv = compiled_source->as_bytecode();
|
const auto spirv = compiled_source->as_bytecode();
|
||||||
|
|
Reference in a new issue