- only MapAsync the staging buffers that are not being used by any command anymore (the commands have finished executing on GPU) - additionally, in WebGPUQueueManager, create a blank new submission state when a new command encoder is generated instead of in submit() BUGS=450620535
113 lines
4.8 KiB
C++
113 lines
4.8 KiB
C++
/*
|
|
* Copyright (C) 2025 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "WebGPUBufferBase.h"
|
|
|
|
#include "WebGPUConstants.h"
|
|
#include "WebGPUQueueManager.h"
|
|
#include "WebGPUStagePool.h"
|
|
|
|
#include "DriverBase.h"
|
|
#include <backend/BufferDescriptor.h>
|
|
|
|
#include <utils/Panic.h>
|
|
#include <utils/debug.h>
|
|
|
|
#include <webgpu/webgpu_cpp.h>
|
|
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
|
|
namespace filament::backend {
|
|
|
|
namespace {
|
|
|
|
// Creates a wgpu::Buffer, ensuring its size is a multiple of FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS.
|
|
// WebGPU's WriteBuffer requires the write size to be a multiple of 4. By ensuring the buffer
|
|
// size is also a multiple of 4, we simplify the update logic.
|
|
[[nodiscard]] wgpu::Buffer createBuffer(wgpu::Device const& device, const wgpu::BufferUsage usage,
|
|
uint32_t size, const char* const label) {
|
|
// Write size must be divisible by WEBGPU_BUFFER_SIZE_MODULUS (e.g. 4).
|
|
// If the whole buffer is written to as is common, so must the buffer size.
|
|
size += (FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - (size % FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS)) %
|
|
FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
|
|
wgpu::BufferDescriptor descriptor{
|
|
.label = label,
|
|
.usage = usage,
|
|
.size = size,
|
|
.mappedAtCreation = false };
|
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
FILAMENT_CHECK_POSTCONDITION(buffer) << "Failed to create buffer for " << label;
|
|
return buffer;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::BufferUsage usage,
|
|
const uint32_t size, char const* const label)
|
|
: mBuffer{ createBuffer(device, usage, size, label) } {}
|
|
|
|
// Updates the GPU buffer with data from a BufferDescriptor.
|
|
// WebGPU requires that the size of the data copied from the staging buffer to the GPU buffer is a
|
|
// multiple of 4. This function handles cases where the buffer descriptor's size is not a multiple
|
|
// of 4 by padding with zeros.
|
|
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
|
const uint32_t byteOffset, wgpu::Device const& device,
|
|
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool) {
|
|
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
|
<< "updateGPUBuffer called with a null buffer";
|
|
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= mBuffer.GetSize())
|
|
<< "Attempting to copy " << bufferDescriptor.size << " bytes into a buffer of size "
|
|
<< mBuffer.GetSize() << " at offset " << byteOffset;
|
|
FILAMENT_CHECK_PRECONDITION(byteOffset % FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS == 0)
|
|
<< "Byte offset must be a multiple of " << FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS
|
|
<< " but is " << byteOffset;
|
|
|
|
// TODO: All buffer objects are created with CopyDst usage.
|
|
// This may have some performance implications. That should be investigated later.
|
|
assert_invariant(mBuffer.GetUsage() & wgpu::BufferUsage::CopyDst);
|
|
|
|
// Calculate some alignment related sizes
|
|
const size_t remainder = bufferDescriptor.size % FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
|
|
const size_t mainBulk = bufferDescriptor.size - remainder;
|
|
const size_t stagingBufferSize =
|
|
remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
|
|
|
|
wgpu::Buffer stagingBuffer = webGPUStagePool->acquireBuffer(stagingBufferSize,
|
|
webGPUQueueManager->getLatestSubmissionState());
|
|
|
|
void* mappedRange = stagingBuffer.GetMappedRange();
|
|
assert_invariant(mappedRange);
|
|
|
|
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
|
|
|
|
// Make sure the padded memory is set to 0 to have deterministic behaviors
|
|
if (remainder != 0) {
|
|
uint8_t* paddingStart = static_cast<uint8_t*>(mappedRange) + bufferDescriptor.size;
|
|
memset(paddingStart, 0, FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - remainder);
|
|
}
|
|
|
|
stagingBuffer.Unmap();
|
|
|
|
// Copy the staging buffer contents to the destination buffer.
|
|
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer,
|
|
byteOffset,
|
|
remainder == 0 ? bufferDescriptor.size
|
|
: mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS);
|
|
}
|
|
|
|
} // namespace filament::backend
|