hibiscus/renderer/rwgpu/rwgpu.cpp

263 lines
8.9 KiB
C++
Raw Normal View History

2023-06-26 18:46:11 +00:00
#define WEBGPU_CPP_IMPLEMENTATION
#include <fmt/format.h>
#include <sstream>
#include <webgpu/webgpu.hpp>
#include <glfw3webgpu/glfw3webgpu.h>
2023-06-28 20:43:01 +00:00
#include <pragmautil.hpp>
2023-06-26 18:46:11 +00:00
#include "rwgpu.hpp"
2023-06-28 20:43:01 +00:00
void onWindowResize(GLFWwindow* window, int width, int height) {
auto that = reinterpret_cast<hibis::rwgpu::RWGPU*>(glfwGetWindowUserPointer(window));
// Resize that so that this can work
if (that != nullptr) that->resize(width, height);
}
2023-06-26 18:46:11 +00:00
namespace hibis::rwgpu {
RWGPU::RWGPU(std::string title, IntVec2 size, LoggerCallback callback) : Renderer(callback) {
wgpu::InstanceDescriptor instanceDescriptor = {};
wgpu::RequestAdapterOptions reqAdaptOpts = {};
wgpu::DeviceDescriptor deviceDescriptor = {};
deviceDescriptor.nextInChain = nullptr;
deviceDescriptor.label = "My Device"; // anything works here, that's your call
deviceDescriptor.requiredFeaturesCount = 0; // we do not require any specific feature
deviceDescriptor.requiredLimits = nullptr; // we do not require any specific limit
deviceDescriptor.defaultQueue.nextInChain = nullptr;
deviceDescriptor.defaultQueue.label = "The default queue";
mWebGPUInstance = wgpu::createInstance(instanceDescriptor);
mWebGPUAdapter = requestAdapter(&reqAdaptOpts);
mWebGPUDevice = requestDevice(&deviceDescriptor);
mWebGPUQueue = mWebGPUDevice.getQueue();
if (!mWebGPUInstance) {
mLogger(Fatal, "Could not initialize WebGPU!");
exit(1);
} else {
const void * address = static_cast<const void*>(mWebGPUInstance);
std::stringstream pointerAsStream;
pointerAsStream << address;
mLogger(Information, fmt::format("WGPU Instance Addr: {}", pointerAsStream.str()));
}
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
mWindow = glfwCreateWindow(size.x, size.y, title.c_str(), NULL, NULL);
2023-06-28 20:43:01 +00:00
glfwSetWindowUserPointer(mWindow, this);
glfwSetFramebufferSizeCallback(mWindow, onWindowResize);
2023-06-26 18:46:11 +00:00
mWebGPUSurface = glfwGetWGPUSurface(mWebGPUInstance, mWindow);
size_t featureCount = wgpuAdapterEnumerateFeatures(mWebGPUAdapter, nullptr);
// Allocate memory (could be a new, or a malloc() if this were a C program)
mWebGPUFeatures.resize(featureCount);
// Call the function a second time, with a non-null return address
wgpuAdapterEnumerateFeatures(mWebGPUAdapter, mWebGPUFeatures.data());
mLogger(Information, "Adapter features: ");
for (wgpu::FeatureName f : mWebGPUFeatures) {
mLogger(Information, fmt::format("WebGPU Feature: {}", f));
}
auto onDeviceError = [](WGPUErrorType type, char const* message, void* /* pUserData */) {
std::cout << "Uncaptured device error: type " << type;
if (message) std::cout << " (" << message << ")";
std::cout << std::endl;
};
wgpuDeviceSetUncapturedErrorCallback(mWebGPUDevice, onDeviceError, nullptr /* pUserData */);
// Not implemented, causes a panic
// auto onQueueWorkDone = [](WGPUQueueWorkDoneStatus status, void* /* pUserData */) {
// std::cout << "Queued work finished with status: " << status << std::endl;
// };
// wgpuQueueOnSubmittedWorkDone(mWebGPUQueue, onQueueWorkDone, nullptr /* pUserData */);
2023-06-28 20:43:01 +00:00
setupSwapChain(size.x, size.y);
2023-06-26 18:46:11 +00:00
}
RWGPU::~RWGPU() {
glfwDestroyWindow(mWindow);
mWebGPUSwapChain.drop();
mWebGPUSurface.drop();
mWebGPUDevice.drop();
mWebGPUAdapter.drop();
mWebGPUInstance.drop();
}
WGPUAdapter RWGPU::requestAdapter(WGPURequestAdapterOptions const * options) {
// A simple structure holding the local information shared with the
// onAdapterRequestEnded callback.
struct UserData {
WGPUAdapter adapter = nullptr;
bool requestEnded = false;
};
UserData userData;
// Callback called by wgpuInstanceRequestAdapter when the request returns
// This is a C++ lambda function, but could be any function defined in the
// global scope. It must be non-capturing (the brackets [] are empty) so
// that it behaves like a regular C function pointer, which is what
// wgpuInstanceRequestAdapter expects (WebGPU being a C API). The workaround
// is to convey what we want to capture through the pUserData pointer,
// provided as the last argument of wgpuInstanceRequestAdapter and received
// by the callback as its last argument.
auto onAdapterRequestEnded = [](WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * pUserData) {
UserData& userData = *reinterpret_cast<UserData*>(pUserData);
if (status == WGPURequestAdapterStatus_Success) {
userData.adapter = adapter;
} else {
std::cout << "Could not get WebGPU adapter: " << message << std::endl;
}
userData.requestEnded = true;
};
// Call to the WebGPU request adapter procedure
wgpuInstanceRequestAdapter(
mWebGPUInstance /* equivalent of navigator.gpu */,
options,
onAdapterRequestEnded,
(void*)&userData
);
// In theory we should wait until onAdapterReady has been called, which
// could take some time (what the 'await' keyword does in the JavaScript
// code). In practice, we know that when the wgpuInstanceRequestAdapter()
// function returns its callback has been called.
assert(userData.requestEnded);
return userData.adapter;
}
WGPUDevice RWGPU::requestDevice(WGPUDeviceDescriptor const * descriptor) {
struct UserData {
WGPUDevice device = nullptr;
bool requestEnded = false;
};
UserData userData;
auto onDeviceRequestEnded = [](WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * pUserData) {
UserData& userData = *reinterpret_cast<UserData*>(pUserData);
if (status == WGPURequestDeviceStatus_Success) {
userData.device = device;
} else {
std::cout << "Could not get WebGPU device: " << message << std::endl;
}
userData.requestEnded = true;
};
wgpuAdapterRequestDevice(
mWebGPUAdapter,
descriptor,
onDeviceRequestEnded,
(void*)&userData
);
assert(userData.requestEnded);
return userData.device;
}
2023-06-28 20:43:01 +00:00
void RWGPU::setupSwapChain(unsigned int width, unsigned int height) {
// Drop swap chain if it currently exists
if (mWebGPUSwapChain) mWebGPUSwapChain.drop();
// Remake swap chain
WGPUSwapChainDescriptor swapChainDesc = {};
swapChainDesc.nextInChain = nullptr;
swapChainDesc.width = width;
swapChainDesc.height = height;
WGPUTextureFormat swapChainFormat = wgpuSurfaceGetPreferredFormat(mWebGPUSurface, mWebGPUAdapter);
swapChainDesc.format = swapChainFormat;
swapChainDesc.usage = WGPUTextureUsage_RenderAttachment;
swapChainDesc.presentMode = WGPUPresentMode_Fifo;
mWebGPUSwapChain = wgpuDeviceCreateSwapChain(mWebGPUDevice, mWebGPUSurface, &swapChainDesc);
}
2023-06-26 18:46:11 +00:00
void RWGPU::clearScreen(Color col) {
mCurrentClearColor = {col.r, col.g, col.b, col.a};
}
void RWGPU::renderCurrent() {
wgpu::TextureView nextTexture = mWebGPUSwapChain.getCurrentTextureView();
if (!nextTexture) {
mLogger(Error, "Couldn't create next texture");
return;
}
WGPUCommandEncoderDescriptor encoderDesc = {};
encoderDesc.nextInChain = nullptr;
encoderDesc.label = "HibisDrawCommandEncoder";
2023-06-28 20:43:01 +00:00
WGPUCommandEncoder encoder = mWebGPUDevice.createCommandEncoder(encoderDesc);
TODO("Implement Deferred Renderer")
2023-06-26 18:46:11 +00:00
WGPURenderPassDescriptor renderPassDesc = {};
WGPURenderPassColorAttachment renderPassColorAttachment = {};
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = &renderPassColorAttachment;
renderPassColorAttachment.view = nextTexture;
renderPassColorAttachment.resolveTarget = nullptr;
renderPassColorAttachment.loadOp = WGPULoadOp_Clear;
renderPassColorAttachment.storeOp = WGPUStoreOp_Store;
renderPassColorAttachment.clearValue = mCurrentClearColor;
renderPassDesc.depthStencilAttachment = nullptr;
renderPassDesc.timestampWriteCount = 0;
renderPassDesc.timestampWrites = nullptr;
renderPassDesc.nextInChain = nullptr;
2023-06-28 20:43:01 +00:00
wgpu::RenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
2023-06-26 18:46:11 +00:00
wgpuRenderPassEncoderEnd(renderPass);
WGPUCommandBufferDescriptor cmdBufferDescriptor = {};
cmdBufferDescriptor.nextInChain = nullptr;
cmdBufferDescriptor.label = "Command buffer";
WGPUCommandBuffer command = wgpuCommandEncoderFinish(encoder, &cmdBufferDescriptor);
wgpuQueueSubmit(mWebGPUQueue, 1, &command);
mWebGPUSwapChain.present();
nextTexture.drop();
}
void RWGPU::drawText(Resource* resource, std::string text, IntVec2 pos, Color color) {}
2023-06-28 20:43:01 +00:00
void RWGPU::drawTexture(Resource* resource, IntRect size) {}
2023-06-26 18:46:11 +00:00
void RWGPU::useShader(Shader* shader, Point2D points[3]) {}
void RWGPU::stopUsingShaders() {}
void RWGPU::preDraw() {}
void RWGPU::postDraw() {}
void RWGPU::update() {
mKeepOpen = !glfwWindowShouldClose(mWindow);
glfwPollEvents();
}
void RWGPU::compileShader(Shader* shader) {}
void RWGPU::toggleWireframe() {}
2023-06-28 20:43:01 +00:00
void RWGPU::setWindowTitle(std::string title) {
glfwSetWindowTitle(mWindow, title.c_str());
}
void RWGPU::resize(unsigned int width, unsigned int height) {
if (width < 0 || height < 0) return;
glfwSetWindowSize(mWindow, width, height);
setupSwapChain(width, height);
}
2023-06-26 18:46:11 +00:00
}