Skip to content

Instantly share code, notes, and snippets.

@ofTheo
Created July 10, 2023 20:25
Show Gist options
  • Save ofTheo/45a66ef335f24c95b044637fbe1530e2 to your computer and use it in GitHub Desktop.
Save ofTheo/45a66ef335f24c95b044637fbe1530e2 to your computer and use it in GitHub Desktop.
Dawn / webGPU test
#include "ofApp.h"
//-------------------------------------------------------------------------------------------
//mostly pulled from CppHelloTriangle.cpp example - void initTextures()
//note the code in this seems to have changed a lot in the last few months - so a more up to date Dawn might need some language changes in here
void ofApp::makeTextureFromOFPixels( ofPixels & pix ) {
auto & device = dawnWindow->mDevice;
dawn::TextureDescriptor descriptor;
descriptor.dimension = dawn::TextureDimension::e2D;
descriptor.size.width = pix.getWidth();
descriptor.size.height = pix.getHeight();
descriptor.size.depth = 1;
descriptor.arrayLayerCount = 1;
descriptor.sampleCount = 1;
cout << " pix is " << pix.getWidth() << " pix " << pix.getHeight() << " channels " << pix.getNumChannels() << endl;
if( pix.getNumChannels() == 4 ){
descriptor.format = dawn::TextureFormat::RGBA8Unorm;
}else{
descriptor.format = dawn::TextureFormat::RGBA8Unorm;
pix.setNumChannels(4);
ofLogWarning("Dawn needs 4 channels");
}
descriptor.mipLevelCount = 1;
descriptor.usage = dawn::TextureUsageBit::CopyDst | dawn::TextureUsageBit::Sampled;
texture = device.CreateTexture(&descriptor);
dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
sampler = device.CreateSampler(&samplerDesc);
dawn::Buffer stagingBuffer = utils::CreateBufferFromData(device, pix.getData(), static_cast<uint32_t>(pix.size()), dawn::BufferUsageBit::CopySrc);
dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0});
dawn::Extent3D copySize = { (uint32_t)pix.getWidth(), (uint32_t)pix.getHeight(), 1};
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
dawn::CommandBuffer copy = encoder.Finish();
dawnQueue.Submit(1, &copy);
}
//-------------------------------------------------------------------------------------------
void ofApp::makeBufferFromOfMesh( ofMesh & mesh ){
auto & device = dawnWindow->mDevice;
//set the index data for the mesh from ofMesh
indexDataOF.clear();
for(auto & index : mesh.getIndices() ){
indexDataOF.push_back( index );
}
size_t sizeIndexes = sizeof(uint32_t) * indexDataOF.size();
indexBuffer = utils::CreateBufferFromData(device, &indexDataOF[0], sizeIndexes, dawn::BufferUsageBit::Index);
//now we make a float vector with all our per-vertex data
//pos, color, normal, texture etc - adds up to 11 floats
vertDataConvOF.resize( mesh.getVertices().size() * 11 );
glm::vec3 c = glm::vec3(1, 1, 1);
glm::vec3 n = glm::vec3(0, 1, 0);
glm::vec2 t = glm::vec2(1, 1);
auto colors = mesh.getColors();
auto normals = mesh.getNormals();
auto texCoords = mesh.getTexCoords();
int i = 0;
int k = 0;
for(auto & v : mesh.getVertices()){
if( k < colors.size() ){
c.r = colors[k].r;
c.g = colors[k].g;
c.b = colors[k].b;
}
if( k < normals.size() ){
n = normals[k];
}
if( k < texCoords.size() ){
t = texCoords[k];
}
//position data
vertDataConvOF[i] = v.x;
vertDataConvOF[i+1] = v.y;
vertDataConvOF[i+2] = v.z;
//color data
vertDataConvOF[i+3] = c.r;
vertDataConvOF[i+4] = c.g;
vertDataConvOF[i+5] = c.b;
//normal data
vertDataConvOF[i+6] = n.x;
vertDataConvOF[i+7] = n.y;
vertDataConvOF[i+8] = n.z;
//texcoord data
vertDataConvOF[i+9] = t.x;
vertDataConvOF[i+10] = 1.0-t.y;
k++;
i+= 11;
}
//now set the per vert data
size_t sizeVerts = sizeof(float) * vertDataConvOF.size();
vertexBuffer = utils::CreateBufferFromData(device, &vertDataConvOF[0], sizeVerts, dawn::BufferUsageBit::Vertex);
}
//--------------------------------------------------------------
void ofApp::setup(){
dawnWindow = dynamic_pointer_cast<ofAppDawnGLFWWindow>( ofGetCurrentWindow() );
if( !dawnWindow ){
return;
}
//hack for now as device can't be copied?
//make shared_pointer?
auto & device = dawnWindow->mDevice;
windowW = dawnWindow->getWidth();
windowH = dawnWindow->getHeight();
cout << " window size is now " << windowW << " " << windowH << endl;
dawnQueue = device.CreateQueue();
swapchain = GetSwapChain(device);
swapchain.Configure(GetPreferredSwapChainTextureFormat(),
dawn::TextureUsageBit::OutputAttachment, windowW, windowH);
//lets load an image :)
ofPixels logoPix;
ofLoadImage(logoPix, "of-gold.png");
makeTextureFromOFPixels(logoPix);
//our vertex shader any in vertex needs to be set in setup()
dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
#version 450
layout(set = 0, binding = 0) uniform cameraData {
mat4 view;
mat4 proj;
} camera;
layout(set = 0, binding = 1) uniform modelData {
mat4 modelMatrix;
};
layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 col;
layout(location = 2) in vec3 nrmal;
layout(location = 3) in vec2 texcoord;
layout(location = 4) out vec3 f_col;
layout(location = 5) out vec2 tcoord_out;
void main() {
vec3 colMod = col;
//fake lighting
vec3 camNrml = vec3(0.5, 0.25, 0.2);
vec3 vNrmal = (camera.proj * camera.view * modelMatrix * vec4(nrmal, 1.0)).xyz;
//
float dotVal = dot(normalize(vNrmal), normalize(camNrml));
colMod *= 0.4 + dotVal * 0.5;
//copy over the texcoord - feels like we should be able to access this in the frag shader directly
tcoord_out = texcoord;
f_col = colMod;
gl_Position = camera.proj * camera.view * modelMatrix * vec4(pos, 1.0);
})"
);
//fragment shader. binding number is what you set in MakeBindGroupLayout
dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
#version 450
layout(set = 0, binding = 2) uniform sampler mySampler;
layout(set = 0, binding = 3) uniform texture2D myTexture;
layout(location = 0) out vec4 fragColor;
layout(location = 4) in vec3 f_col;
layout(location = 5) in vec2 tcoord_out;
void main() {
fragColor = texture(sampler2D(myTexture, mySampler), tcoord_out) * vec4(f_col, 1.0);
})");
//our per-vertex data
//4 attributes for position, color, normals and tex coords
utils::ComboVertexInputDescriptor vertexInput;
vertexInput.cBuffers[0].attributeCount = 4;
vertexInput.cAttributes[0].format = dawn::VertexFormat::Float3;
//for our colors
vertexInput.cAttributes[1].shaderLocation = 1;
vertexInput.cAttributes[1].offset = 3 * sizeof(float);
vertexInput.cAttributes[1].format = dawn::VertexFormat::Float3;
//for our normals ( added )
vertexInput.cAttributes[2].shaderLocation = 2;
vertexInput.cAttributes[2].offset = 6 * sizeof(float);
vertexInput.cAttributes[2].format = dawn::VertexFormat::Float3;
//for our tex coords ( added )
vertexInput.cAttributes[3].shaderLocation = 3;
vertexInput.cAttributes[3].offset = 9 * sizeof(float);
vertexInput.cAttributes[3].format = dawn::VertexFormat::Float2;
vertexInput.bufferCount = 1;
//size of each vert including pos (3) / color (3) / normal (3) / texcoord (2)
vertexInput.cBuffers[0].stride = 11 * sizeof(float);
//this is the result of combining two Dawn examples :)
//not sure if we need the second Vertex buffer
auto bgl = utils::MakeBindGroupLayout(
device, {
{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
{1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer},
{2, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler},
{3, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture},
});
dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl);
//camera stuff
dawn::BufferDescriptor cameraBufDesc;
cameraBufDesc.size = sizeof(CameraData);
cameraBufDesc.usage = dawn::BufferUsageBit::CopyDst | dawn::BufferUsageBit::Uniform;
cameraBuffer = device.CreateBuffer(&cameraBufDesc);
glm::mat4 transform(1.0);
transformBuffer[0] = utils::CreateBufferFromData(device, &transform, sizeof(glm::mat4), dawn::BufferUsageBit::Uniform);
transform = glm::translate(transform, glm::vec3(0.f, -2.f, 0.f));
transformBuffer[1] = utils::CreateBufferFromData(device, &transform, sizeof(glm::mat4), dawn::BufferUsageBit::Uniform);
//dont quite understand this all - result of combining two examples
dawn::TextureView view = texture.CreateDefaultView();
bindGroup[0] = utils::MakeBindGroup(device, bgl, {
{0, cameraBuffer, 0, sizeof(CameraData)},
{1, transformBuffer[0], 0, sizeof(glm::mat4)},
{2, sampler},
{3, view}
});
depthStencilView = CreateDefaultDepthStencilView(device, windowW, windowH);
utils::ComboRenderPipelineDescriptor descriptor(device);
descriptor.layout = pl;
descriptor.cVertexStage.module = vsModule;
descriptor.cFragmentStage.module = fsModule;
descriptor.vertexInput = &vertexInput;
descriptor.depthStencilState = &descriptor.cDepthStencilState;
descriptor.cDepthStencilState.format = dawn::TextureFormat::Depth24PlusStencil8;
descriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat();
descriptor.cDepthStencilState.depthWriteEnabled = true;
descriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less;
pipeline = device.CreateRenderPipeline(&descriptor);
utils::ComboRenderPipelineDescriptor pDescriptor(device);
pDescriptor.layout = pl;
pDescriptor.cVertexStage.module = vsModule;
pDescriptor.cFragmentStage.module = fsModule;
pDescriptor.vertexInput = &vertexInput;
pDescriptor.depthStencilState = &pDescriptor.cDepthStencilState;
pDescriptor.cDepthStencilState.format = dawn::TextureFormat::Depth24PlusStencil8;
pDescriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat();
pDescriptor.cDepthStencilState.stencilFront.passOp = dawn::StencilOperation::Replace;
pDescriptor.cDepthStencilState.stencilBack.passOp = dawn::StencilOperation::Replace;
pDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less;
planePipeline = device.CreateRenderPipeline(&pDescriptor);
//camera settings - fov, aspect ratio, near and far
cameraData.proj = glm::perspective(glm::radians(45.0f), (float)windowW/((float)windowH), 1.0f, 100.0f);
mesh = ofMesh::box(2, 2, 2, 2,2,2);
makeBufferFromOfMesh(mesh);
}
struct {uint32_t a; float b;} s;
//--------------------------------------------------------------
void ofApp::update(){
//hack for now as device can't be copied?
//make shared_pointer?
auto & device = dawnWindow->mDevice;
s.a = (s.a + 1) % 256;
s.b += 0.01f * ofGetLastFrameTime() * 60.0;
if (s.b >= 10.0f) {s.b = 0.0f;}
//look around
cameraData.view = glm::lookAt(
glm::vec3(8.f * std::sin(glm::radians(s.b * 4.0f * 36.f)), -2.0f - std::sin(glm::radians(s.b * 0.5 * 360.f)) * 1.2, 8.f * std::cos(glm::radians(s.b * 4.0 * 36.f))),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f)
);
cameraBuffer.SetSubData(0, sizeof(CameraData), &cameraData);
dawn::Texture backbuffer = swapchain.GetNextTexture();
utils::ComboRenderPassDescriptor renderPass({backbuffer.CreateDefaultView()},
depthStencilView);
if( meshMode != preMeshMode ){
if( meshMode == 1 ){
mesh = ofMesh::box(2, 2, 2, 2,2,2);
}else if( meshMode == 2 ){
mesh = ofMesh::box(0.25, 4, 2, 2,2,2);
}else if( meshMode == 3 ){
mesh = ofMesh::cylinder(1, 2, 36, 12, 2, true, OF_PRIMITIVE_TRIANGLES);
}else if( meshMode == 4 ){
mesh = ofMesh::cone(1, 2, 36, 12, 2, OF_PRIMITIVE_TRIANGLES);
}else if( meshMode == 5 ){
mesh = ofMesh::icosphere(2, 3);
}else if( meshMode == 6 ){
auto m = ofPlanePrimitive(4, 4, 4, 4, OF_PRIMITIVE_TRIANGLES);
mesh = m.getMesh();
}
preMeshMode = meshMode;
int numIndexes = mesh.getIndices().size();
makeBufferFromOfMesh(mesh);
}
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
{
static const uint64_t vertexBufferOffsets[1] = {0};
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup[0], 0, nullptr);
pass.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets);
pass.SetIndexBuffer(indexBuffer, 0);
pass.DrawIndexed(indexDataOF.size(), 1, 0, 0, 0);
pass.EndPass();
}
dawn::CommandBuffer commands = encoder.Finish();
dawnQueue.Submit(1, &commands);
swapchain.Present(backbuffer);
}
//--------------------------------------------------------------
void ofApp::draw(){
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key){
if( key >= '1' && key <= '9' ){
meshMode = key-48;
}
}
//--------------------------------------------------------------
void ofApp::keyReleased(int key){
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment