Repository: google/filament
File: filament/backend/src/webgpu/WebGPUDriver.cpp (C++)

Code before:
        .depthOrArrayLayers = 1 };
    encoder.CopyTextureToBuffer(&source, &destination, &copySize);
    wgpu::CommandBuffer commandBuffer = encoder.Finish();
    mQueue.Submit(1, &commandBuffer);

    // Map the buffer to read the data
    struct UserData {
        PixelBufferDescriptor pbd;
        wgpu::Buffer buffer;
        size_t unpaddedBytesPerRow;
        size_t paddedBytesPerRow;
        uint32_t height;
        WebGPUDriver* that;
    };
    auto userData = std::make_unique<UserData>(UserData{ .pbd = std::move(pixelBufferDescriptor),
        .buffer = std::move(stagingBuffer),
        .unpaddedBytesPerRow = unpaddedBytesPerRow,
        .paddedBytesPerRow = paddedBytesPerRow,
        .height = height,
        .that = this });

    userData->buffer.MapAsync(
            wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous,
            [](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
                std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
                if (status == wgpu::MapAsyncStatus::Success) {
                    const char* src = static_cast<const char*>(
                            data->buffer.GetConstMappedRange(0, data->buffer.GetSize()));
                    char* dst = static_cast<char*>(data->pbd.buffer);
                    for (uint32_t i = 0; i < data->height; ++i) {
                        memcpy(dst + i * data->unpaddedBytesPerRow,
                                src + i * data->paddedBytesPerRow, data->unpaddedBytesPerRow);
                    }
                    data->buffer.Unmap();
                } else {
                    FWGPU_LOGE << "Failed to map staging buffer for readPixels: " << message;
                }
                data->that->scheduleDestroy(std::move(data->pbd));
            },
            userData.release());
}

void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> bufferObjectHandle,
        const uint32_t offset, const uint32_t size, backend::BufferDescriptor&& bufferDescriptor) {
    // todo
    scheduleDestroy(std::move(bufferDescriptor));
}

void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
        Handle<HwRenderTarget> destinationRenderTargetHandle, const Viewport destinationViewport,
        Handle<HwRenderTarget> sourceRenderTargetHandle, const Viewport sourceViewport,

Review comment (bug):
I wonder if this could/would lead to a race condition with the caller. Wouldn't the caller want to know somehow that the texture has finished being written before using it? I wonder if ultimately some kind of synchronization mechanism would make sense, such as a field on HwTexture or a callback in this function or something. I realize this is more of a question for the other Filament folks. What do you think @poweifeng ?

Code after:
    wgpu::Extent3D copySize{ .width = actualWidth,
        .height = actualHeight,
        .depthOrArrayLayers = 1 };
    encoder.CopyTextureToBuffer(&source, &destination, &copySize);
    wgpu::CommandBuffer commandBuffer = encoder.Finish();
    mQueue.Submit(1, &commandBuffer);

    // Map the buffer to read the data
    struct UserData {
        PixelBufferDescriptor pixelBufferDescriptor;
        wgpu::Buffer buffer;
        size_t unpaddedBytesPerRow;
        size_t paddedBytesPerRow;
        uint32_t height;
        WebGPUDriver* driver;
    };
    auto userData = std::make_unique<UserData>(UserData{
        .pixelBufferDescriptor = std::move(pixelBufferDescriptor),
        .buffer = std::move(stagingBuffer),
        .unpaddedBytesPerRow = unpaddedBytesPerRow,
        .paddedBytesPerRow = paddedBytesPerRow,
        .height = height,
        .driver = this,
    });

    userData->buffer.MapAsync(
            wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous,
            [](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
                std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
                if (status == wgpu::MapAsyncStatus::Success) {
                    const char* src = static_cast<const char*>(
                            data->buffer.GetConstMappedRange(0, data->buffer.GetSize()));
                    char* dst = static_cast<char*>(data->pixelBufferDescriptor.buffer);
                    for (uint32_t i = 0; i < data->height; ++i) {
                        memcpy(dst + i * data->unpaddedBytesPerRow,
                                src + i * data->paddedBytesPerRow, data->unpaddedBytesPerRow);
                    }
                    data->buffer.Unmap();
                } else {
                    FWGPU_LOGE << "Failed to map staging buffer for readPixels: " << message;
                }
                data->driver->scheduleDestroy(std::move(data->pixelBufferDescriptor));
            },
            userData.release());
}

void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> bufferObjectHandle,
        const uint32_t offset, const uint32_t size, backend::BufferDescriptor&& bufferDescriptor) {
    // todo
    scheduleDestroy(std::move(bufferDescriptor));
}