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
|
|
|
}
|