2022-02-18 16:57:00 -05:00
# include "gfx_webgpu.hpp"
2022-02-20 20:05:05 -05:00
# 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"
2022-02-20 22:28:07 -05:00
# include "utility.hpp"
2022-02-20 20:05:05 -05:00
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 ) ;
}
2022-02-18 17:19:37 -05:00
2022-02-18 16:57:00 -05:00
bool GFXWebGPU : : initialize ( const GFXCreateInfo & createInfo ) {
2022-02-20 20:05:05 -05:00
device = emscripten_webgpu_get_device ( ) ;
if ( device = = nullptr ) {
prism : : log ( " Failed to get WebGPU device! " ) ;
}
queue = wgpuDeviceGetQueue ( device ) ;
swapchain = create_swapchain ( ) ;
2022-02-18 17:19:37 -05:00
prism : : log ( " Initialized WebGPU! " ) ;
2022-02-18 16:57:00 -05:00
return true ;
}
2022-02-20 22:28:07 -05:00
ShaderLanguage GFXWebGPU : : accepted_shader_language ( ) {
return ShaderLanguage : : WGSL ;
}
2022-02-18 16:57:00 -05:00
const char * GFXWebGPU : : get_name ( ) {
return " WebGPU " ;
2022-02-20 20:05:05 -05:00
}
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 ;
2022-03-06 19:38:17 -05:00
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 ;
2022-02-20 20:05:05 -05:00
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 {
2022-03-06 19:38:17 -05:00
auto vertex_shader = prism : : open_file ( prism : : internal_domain / ( info . shaders . vertex_src . as_path ( ) . string ( ) + " .wgsl.spv " ) , true ) ;
2022-02-20 20:05:05 -05:00
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 {
2022-03-06 19:38:17 -05:00
auto fragment_shader = prism : : open_file ( prism : : internal_domain / ( info . shaders . fragment_src . as_path ( ) . string ( ) + " .wgsl.spv " ) , true ) ;
2022-02-20 20:05:05 -05:00
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 ( ) ) ;
}
}
2022-02-20 22:28:07 -05:00
prism : : log ( " building pipeline {} " , info . label ) ;
// 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 ;
2022-02-20 20:05:05 -05:00
std : : vector < std : : vector < WGPUVertexAttribute > > attributes ;
2022-02-20 22:28:07 -05:00
attributes . resize ( dummy_buffer_count ) ;
prism : : log ( " dummy buffer count: {} " , dummy_buffer_count ) ;
2022-02-20 20:05:05 -05:00
for ( auto & attribute : info . vertex_input . attributes ) {
WGPUVertexAttribute description ;
description . shaderLocation = attribute . location ;
description . format = toVertFormat ( attribute . format ) ;
description . offset = attribute . offset ;
2022-02-20 22:28:07 -05:00
prism : : log ( " {} of {} " , attribute . binding , info . vertex_input . inputs . size ( ) ) ;
2022-02-20 20:05:05 -05:00
attributes [ attribute . binding ] . push_back ( description ) ;
}
std : : vector < WGPUVertexBufferLayout > inputs ;
2022-02-20 22:28:07 -05:00
inputs . resize ( dummy_buffer_count ) ;
2022-02-20 20:05:05 -05:00
for ( auto & binding : info . vertex_input . inputs ) {
2022-02-20 22:28:07 -05:00
prism : : log ( " binding loc {} " , binding . location ) ;
prism : : log ( " debugging attributes at loc {} " , binding . location ) ;
for ( auto attr : attributes [ binding . location ] ) {
prism : : log ( " - attrib {} " , attr . shaderLocation ) ;
prism : : log ( " fmt: {} " , utility : : enum_to_string ( attr . format ) ) ;
}
2022-02-20 20:05:05 -05:00
WGPUVertexBufferLayout b ;
b . attributes = attributes [ binding . location ] . data ( ) ;
2022-02-20 22:28:07 -05:00
b . attributeCount = attributes [ binding . location ] . size ( ) ;
2022-02-20 20:05:05 -05:00
b . arrayStride = binding . stride ;
b . stepMode = WGPUVertexStepMode_Vertex ;
2022-02-20 22:28:07 -05:00
inputs [ binding . location ] = b ;
2022-02-20 20:05:05 -05:00
}
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 ;
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 {
2022-03-06 19:38:17 -05:00
auto compute_shader = prism : : open_file ( prism : : internal_domain / ( info . compute_src . as_path ( ) . string ( ) + " .wgsl.spv " ) , true ) ;
2022-02-20 20:05:05 -05:00
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 ) {
2022-03-06 19:38:17 -05:00
WGPUTextureView backBufView = wgpuSwapChainGetCurrentTextureView ( swapchain ) ;
2022-02-20 20:05:05 -05:00
WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder ( device , nullptr ) ;
WGPUCommandBuffer commands = wgpuCommandEncoderFinish ( encoder , nullptr ) ;
wgpuQueueSubmit ( queue , 1 , & commands ) ;
wgpuCommandBufferRelease ( commands ) ;
wgpuTextureViewRelease ( backBufView ) ;
2022-02-18 16:57:00 -05:00
}