Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 2 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ export function HelloTriangle() {
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

context.present();
};
helloTriangle();
}, [ref]);
Expand Down Expand Up @@ -164,16 +162,8 @@ ctx.canvas.height = ctx.canvas.clientHeight * PixelRatio.get();

### Frame Scheduling

In React Native, we want to keep frame presentation as a manual operation as we plan to provide more advanced rendering options that are React Native specific.
This means that when you are ready to present a frame, you need to call `present` on the context.

```tsx
// draw
// submit to the queue
device.queue.submit([commandEncoder.finish()]);
// This method is React Native only
context.present();
```
Frames are presented automatically once you submit command buffers to the device queue, matching the behaviour on the web.
The native context still exposes a `present()` method for backwards compatibility, but calling it is optional and typically a no-op.

### External Textures

Expand Down
2 changes: 1 addition & 1 deletion apps/example/src/ThreeJS/Cube.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const Cube = () => {
mesh.rotation.y = time / 1000;

renderer.render(scene, camera);
context.present();
//context.present();
}
renderer.setAnimationLoop(animate);
return () => {
Expand Down
2 changes: 1 addition & 1 deletion apps/example/src/components/useWebGPU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const useWebGPU = (scene: Scene) => {
const render = () => {
const timestamp = Date.now();
renderScene(timestamp);
context.present();
//context.present();
animationFrameId.current = requestAnimationFrame(render);
};

Expand Down
14 changes: 2 additions & 12 deletions packages/webgpu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ export function HelloTriangle() {
passEncoder.end();

device.queue.submit([commandEncoder.finish()]);

context.present();
};
helloTriangle();
}, [ref]);
Expand Down Expand Up @@ -164,16 +162,8 @@ ctx.canvas.height = ctx.canvas.clientHeight * PixelRatio.get();

### Frame Scheduling

In React Native, we want to keep frame presentation as a manual operation as we plan to provide more advanced rendering options that are React Native specific.
This means that when you are ready to present a frame, you need to call `present` on the context.

```tsx
// draw
// submit to the queue
device.queue.submit([commandEncoder.finish()]);
// This method is React Native only
context.present();
```
Frames are presented automatically once you submit command buffers to the device queue, matching the behaviour on the web.
The native context still exposes a `present()` method for backwards compatibility, but calling it is optional and typically a no-op.

### External Textures

Expand Down
80 changes: 75 additions & 5 deletions packages/webgpu/cpp/rnwgpu/SurfaceRegistry.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#pragma once

#include <memory>
#include <optional>
#include <shared_mutex>
#include <unordered_map>
#include <utility>
#include <vector>

#ifdef __APPLE__
#include <dispatch/dispatch.h>
Expand All @@ -12,6 +14,16 @@

#include "webgpu/webgpu_cpp.h"

#include "api/Canvas.h"

#ifdef __APPLE__
namespace dawn::native::metal {

void WaitForCommandsToBeScheduled(WGPUDevice device);

} // namespace dawn::native::metal
#endif

namespace rnwgpu {

struct NativeInfo {
Expand All @@ -32,6 +44,14 @@ class SurfaceInfo {

~SurfaceInfo() { surface = nullptr; }

struct PresentRequest {
wgpu::Device device;
wgpu::Surface surface;
int width;
int height;
std::weak_ptr<Canvas> canvas;
};

void reconfigure(int newWidth, int newHeight) {
std::unique_lock<std::shared_mutex> lock(_mutex);
config.width = newWidth;
Expand All @@ -45,6 +65,7 @@ class SurfaceInfo {
config.width = width;
config.height = height;
config.presentMode = wgpu::PresentMode::Fifo;
_needsPresent = false;
_configure();
}

Expand All @@ -55,6 +76,7 @@ class SurfaceInfo {
} else {
texture = nullptr;
}
_needsPresent = false;
}

void *switchToOffscreen() {
Expand All @@ -72,6 +94,7 @@ class SurfaceInfo {
texture = config.device.CreateTexture(&textureDesc);
}
surface = nullptr;
_needsPresent = false;
return nativeSurface;
}

Expand Down Expand Up @@ -110,6 +133,7 @@ class SurfaceInfo {
surface.Present();
texture = nullptr;
}
_needsPresent = false;
}

void resize(int newWidth, int newHeight) {
Expand All @@ -118,24 +142,39 @@ class SurfaceInfo {
height = newHeight;
}

void present() {
void setCanvas(std::weak_ptr<Canvas> canvas) {
std::unique_lock<std::shared_mutex> lock(_mutex);
if (surface) {
surface.Present();
}
_canvas = std::move(canvas);
}

wgpu::Texture getCurrentTexture() {
std::shared_lock<std::shared_mutex> lock(_mutex);
std::unique_lock<std::shared_mutex> lock(_mutex);
if (surface) {
wgpu::SurfaceTexture surfaceTexture;
surface.GetCurrentTexture(&surfaceTexture);
_needsPresent = true;
return surfaceTexture.texture;
} else {
_needsPresent = false;
return texture;
}
}

std::optional<PresentRequest> takePendingPresent() {
std::unique_lock<std::shared_mutex> lock(_mutex);
if (!_needsPresent || !surface) {
_needsPresent = false;
return std::nullopt;
}
PresentRequest request{.device = config.device,
.surface = surface,
.width = width,
.height = height,
.canvas = _canvas};
_needsPresent = false;
return request;
}

NativeInfo getNativeInfo() {
std::shared_lock<std::shared_mutex> lock(_mutex);
return {.nativeSurface = nativeSurface, .width = width, .height = height};
Expand Down Expand Up @@ -191,13 +230,15 @@ class SurfaceInfo {
}

mutable std::shared_mutex _mutex;
std::weak_ptr<Canvas> _canvas;
void *nativeSurface = nullptr;
wgpu::Surface surface = nullptr;
wgpu::Texture texture = nullptr;
wgpu::Instance gpu;
wgpu::SurfaceConfiguration config;
int width;
int height;
bool _needsPresent = false;
};

class SurfaceRegistry {
Expand Down Expand Up @@ -244,6 +285,35 @@ class SurfaceRegistry {
return info;
}

void presentPendingSurfaces() {
std::vector<std::shared_ptr<SurfaceInfo>> infosCopy;
{
std::shared_lock<std::shared_mutex> lock(_mutex);
infosCopy.reserve(_registry.size());
for (auto &entry : _registry) {
infosCopy.push_back(entry.second);
}
}

for (auto &info : infosCopy) {
auto request = info->takePendingPresent();
if (!request.has_value()) {
continue;
}

auto presentRequest = std::move(request.value());
#ifdef __APPLE__
dawn::native::metal::WaitForCommandsToBeScheduled(
presentRequest.device.Get());
#endif
if (auto canvas = presentRequest.canvas.lock()) {
canvas->setClientWidth(presentRequest.width);
canvas->setClientHeight(presentRequest.height);
}
presentRequest.surface.Present();
}
}

private:
SurfaceRegistry() = default;
mutable std::shared_mutex _mutex;
Expand Down
13 changes: 8 additions & 5 deletions packages/webgpu/cpp/rnwgpu/api/GPUCanvasContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,17 @@ std::shared_ptr<GPUTexture> GPUCanvasContext::getCurrentTexture() {
}

void GPUCanvasContext::present() {
auto request = _surfaceInfo->takePendingPresent();
if (!request.has_value()) {
return;
}
#ifdef __APPLE__
dawn::native::metal::WaitForCommandsToBeScheduled(
_surfaceInfo->getDevice().Get());
request->device.Get());
#endif
auto size = _surfaceInfo->getSize();
_canvas->setClientWidth(size.width);
_canvas->setClientHeight(size.height);
_surfaceInfo->present();
_canvas->setClientWidth(request->width);
_canvas->setClientHeight(request->height);
request->surface.Present();
}

} // namespace rnwgpu
1 change: 1 addition & 0 deletions packages/webgpu/cpp/rnwgpu/api/GPUCanvasContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class GPUCanvasContext : public m::HybridObject {
auto &registry = rnwgpu::SurfaceRegistry::getInstance();
_surfaceInfo =
registry.getSurfaceInfoOrCreate(contextId, _gpu->get(), width, height);
_surfaceInfo->setCanvas(_canvas);
}

public:
Expand Down
3 changes: 3 additions & 0 deletions packages/webgpu/cpp/rnwgpu/api/GPUQueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

#include "Convertors.h"

#include "../SurfaceRegistry.h"

namespace rnwgpu {

struct BufferSource {
Expand All @@ -26,6 +28,7 @@ void GPUQueue::submit(
return;
}
_instance.Submit(bufs_size, bufs.data());
SurfaceRegistry::getInstance().presentPendingSurfaces();
}

void GPUQueue::writeBuffer(std::shared_ptr<GPUBuffer> buffer,
Expand Down
6 changes: 5 additions & 1 deletion packages/webgpu/src/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export interface NativeCanvas {
}

export type RNCanvasContext = GPUCanvasContext & {
present: () => void;
/**
* @deprecated Presentation happens automatically after queue submission.
* This method is kept for backwards compatibility and is a no-op.
*/
present?: () => void;
};

export interface CanvasRef {
Expand Down
Loading