Skip to content

Commit

Permalink
[WebGPU] Running api,validation,image_copy,* crashes the GPU process
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=268356
<radar://121902940>

Reviewed by Tadeu Zagallo.

Having api,validation,image_copy,* pass is tracked by
https://bugs.webkit.org/show_bug.cgi?id=267284

Right now, apply a smaller set of changes such
that running the test does not crash the GPU process.

* Source/WebGPU/WebGPU/CommandEncoder.mm:
(WebGPU::CommandEncoder::copyBufferToTexture):
(WebGPU::CommandEncoder::copyTextureToBuffer):
* Source/WebGPU/WebGPU/Queue.mm:
(WebGPU::Queue::writeTexture):
* Source/WebGPU/WebGPU/Texture.mm:
(WebGPU::Texture::aspectSpecificFormat):

Canonical link: https://commits.webkit.org/274076@main
  • Loading branch information
mwyrzykowski committed Feb 4, 2024
1 parent de59fe7 commit 0b6b7cf
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 9 deletions.
33 changes: 32 additions & 1 deletion Source/WebGPU/WebGPU/CommandEncoder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,22 @@ static bool validateCopyBufferToTexture(const WGPUImageCopyBuffer& source, const
if (sourceBytesPerRow == WGPU_COPY_STRIDE_UNDEFINED)
sourceBytesPerRow = sourceBuffer.length;

auto blockSize = Texture::texelBlockSize(Texture::aspectSpecificFormat(destinationTexture.format(), destination.aspect));
switch (destinationTexture.dimension()) {
case WGPUTextureDimension_1D:
sourceBytesPerRow = std::min<uint32_t>(sourceBytesPerRow, blockSize * m_device->limits().maxTextureDimension1D);
break;
case WGPUTextureDimension_2D:
sourceBytesPerRow = std::min<uint32_t>(sourceBytesPerRow, blockSize * m_device->limits().maxTextureDimension2D);
break;
case WGPUTextureDimension_3D:
sourceBytesPerRow = std::min<uint32_t>(sourceBytesPerRow, blockSize * m_device->limits().maxTextureDimension3D);
break;
case WGPUTextureDimension_Force32:
break;
}


MTLBlitOption options = MTLBlitOptionNone;
switch (destination.aspect) {
case WGPUTextureAspect_All:
Expand Down Expand Up @@ -1203,7 +1219,7 @@ static bool validateCopyTextureToBuffer(const WGPUImageCopyTexture& source, cons
return;
}

auto logicalSize = fromAPI(source.texture).logicalMiplevelSpecificTextureExtent(source.mipLevel);
auto logicalSize = sourceTexture.logicalMiplevelSpecificTextureExtent(source.mipLevel);
auto widthForMetal = std::min(copySize.width, logicalSize.width);
auto heightForMetal = std::min(copySize.height, logicalSize.height);
auto depthForMetal = std::min(copySize.depthOrArrayLayers, logicalSize.depthOrArrayLayers);
Expand All @@ -1214,6 +1230,21 @@ static bool validateCopyTextureToBuffer(const WGPUImageCopyTexture& source, cons
if (destinationBytesPerRow == WGPU_COPY_STRIDE_UNDEFINED)
destinationBytesPerRow = destinationBuffer.length;

auto blockSize = Texture::texelBlockSize(Texture::aspectSpecificFormat(sourceTexture.format(), source.aspect));
switch (sourceTexture.dimension()) {
case WGPUTextureDimension_1D:
destinationBytesPerRow = std::min<uint32_t>(destinationBytesPerRow, blockSize * m_device->limits().maxTextureDimension1D);
break;
case WGPUTextureDimension_2D:
destinationBytesPerRow = std::min<uint32_t>(destinationBytesPerRow, blockSize * m_device->limits().maxTextureDimension2D);
break;
case WGPUTextureDimension_3D:
destinationBytesPerRow = std::min<uint32_t>(destinationBytesPerRow, blockSize * m_device->limits().maxTextureDimension3D);
break;
case WGPUTextureDimension_Force32:
break;
}

auto rowsPerImage = destination.layout.rowsPerImage;
if (rowsPerImage == WGPU_COPY_STRIDE_UNDEFINED)
rowsPerImage = heightForMetal ?: 1;
Expand Down
46 changes: 42 additions & 4 deletions Source/WebGPU/WebGPU/Queue.mm
Original file line number Diff line number Diff line change
Expand Up @@ -409,11 +409,20 @@ static bool validateWriteTexture(const WGPUImageCopyTexture& destination, const
// https://gpuweb.github.io/gpuweb/#dom-gpuqueue-writetexture

auto dataByteSize = dataSize;
if (!dataByteSize) {
device->generateAValidationError("GPUQueue.writeTexture: data byte size is zero"_s);
return;
}

auto& texture = fromAPI(destination.texture);
auto textureFormat = texture.format();
if (Texture::isDepthOrStencilFormat(textureFormat))
if (Texture::isDepthOrStencilFormat(textureFormat)) {
textureFormat = Texture::aspectSpecificFormat(textureFormat, destination.aspect);
if (textureFormat == WGPUTextureFormat_Undefined) {
device->generateAValidationError("Invalid depth-stencil format"_s);
return;
}
}

RELEASE_ASSERT(data);
if (!validateWriteTexture(destination, dataLayout, size, dataByteSize, texture) || !isValidToUseWith(texture, *this)) {
Expand All @@ -437,6 +446,20 @@ static bool validateWriteTexture(const WGPUImageCopyTexture& destination, const
if (bytesPerRow == WGPU_COPY_STRIDE_UNDEFINED)
bytesPerRow = size.height ? (dataSize / size.height) : dataSize;

switch (texture.dimension()) {
case WGPUTextureDimension_1D:
bytesPerRow = std::min<uint32_t>(bytesPerRow, blockSize * device->limits().maxTextureDimension1D);
break;
case WGPUTextureDimension_2D:
bytesPerRow = std::min<uint32_t>(bytesPerRow, blockSize * device->limits().maxTextureDimension2D);
break;
case WGPUTextureDimension_3D:
bytesPerRow = std::min<uint32_t>(bytesPerRow, blockSize * device->limits().maxTextureDimension3D);
break;
case WGPUTextureDimension_Force32:
break;
}

NSUInteger rowsPerImage = (dataLayout.rowsPerImage == WGPU_COPY_STRIDE_UNDEFINED) ? size.height : dataLayout.rowsPerImage;
NSUInteger bytesPerImage = bytesPerRow * rowsPerImage;

Expand Down Expand Up @@ -506,7 +529,6 @@ static bool validateWriteTexture(const WGPUImageCopyTexture& destination, const
return;
}

ASSERT(heightForMetal == 1);
bytesPerRow = 0;
bytesPerImage = 0;
}
Expand All @@ -520,10 +542,23 @@ static bool validateWriteTexture(const WGPUImageCopyTexture& destination, const
auto maxZ = std::max<size_t>(1, size.depthOrArrayLayers);
newData.resize(newBytesPerImage * maxZ);
memset(&newData[0], 0, newData.size());

auto verticalOffset = checkedProduct<uint64_t>(maxY ? (maxY - 1) : 0, bytesPerRow);
ASSERT(maxZ);
auto depthOffset = checkedProduct<uint64_t>(maxZ - 1, bytesPerImage);
auto maxResult = checkedSum<uint64_t>(verticalOffset.value(), depthOffset.value(), newBytesPerRow);
if (verticalOffset.hasOverflowed() || depthOffset.hasOverflowed() || maxResult.hasOverflowed()) {
device->generateAValidationError("Result overflows uin64_t"_s);
return;
}

for (size_t z = 0; z < maxZ; ++z) {
for (size_t y = 0; y < maxY; ++y) {
auto sourceBytes = static_cast<const uint8_t*>(data) + y * bytesPerRow + z * bytesPerImage;
RELEASE_ASSERT(y * bytesPerRow + z * bytesPerImage + newBytesPerRow <= dataByteSize);
if (y * bytesPerRow + z * bytesPerImage + newBytesPerRow > dataByteSize) {
device->generateAValidationError("dataByteSize was too small"_s);
return;
}
auto destBytes = &newData[0] + y * newBytesPerRow + z * newBytesPerImage;
memcpy(destBytes, sourceBytes, newBytesPerRow);
}
Expand Down Expand Up @@ -614,7 +649,10 @@ static bool validateWriteTexture(const WGPUImageCopyTexture& destination, const
ensureBlitCommandEncoder();
// FIXME(PERFORMANCE): Suballocate, so the common case doesn't need to hit the kernel.
// FIXME(PERFORMANCE): Should this temporary buffer really be shared?
auto newBufferSize = static_cast<NSUInteger>(dataByteSize - dataLayout.offset);
auto newBufferSize = static_cast<NSUInteger>(dataByteSize);
if (!newBufferSize)
return;

bool noCopy = newBufferSize >= largeBufferSize;
id<MTLBuffer> temporaryBuffer = noCopy ? [device->device() newBufferWithBytesNoCopy:static_cast<char*>(data) + dataLayout.offset length:newBufferSize options:MTLResourceStorageModeShared deallocator:nil] : [device->device() newBufferWithBytes:static_cast<char*>(data) + dataLayout.offset length:newBufferSize options:MTLResourceStorageModeShared];
if (!temporaryBuffer)
Expand Down
6 changes: 2 additions & 4 deletions Source/WebGPU/WebGPU/Texture.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2395,13 +2395,11 @@ static bool textureViewFormatCompatible(WGPUTextureFormat format1, WGPUTextureFo
return format;
case WGPUTextureAspect_StencilOnly: {
auto result = stencilSpecificFormat(format);
ASSERT(result);
return *result;
return result ? *result : WGPUTextureFormat_Undefined;
}
case WGPUTextureAspect_DepthOnly: {
auto result = depthSpecificFormat(format);
ASSERT(result);
return *result;
return result ? *result : WGPUTextureFormat_Undefined;
}
case WGPUTextureAspect_Force32:
ASSERT_NOT_REACHED();
Expand Down

0 comments on commit 0b6b7cf

Please sign in to comment.