blob: 1fef2cfb230ed3a5bb456828fd061a89980974aa [file] [log] [blame] [edit]
// Copyright 2023 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <fcntl.h>
#include <gbm.h>
#include <unistd.h>
#include <vulkan/vulkan.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "dawn/tests/end2end/SharedTextureMemoryTests.h"
#include "dawn/webgpu_cpp.h"
namespace dawn {
namespace {
template <wgpu::FeatureName FenceFeature>
class Backend : public SharedTextureMemoryTestBackend {
public:
static SharedTextureMemoryTestBackend* GetInstance() {
static Backend b;
return &b;
}
std::string Name() const override {
switch (FenceFeature) {
case wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD:
return "dma buf, opaque fd";
case wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD:
return "dma buf, sync fd";
default:
DAWN_UNREACHABLE();
}
}
std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter&) const override {
return {wgpu::FeatureName::SharedTextureMemoryDmaBuf,
wgpu::FeatureName::DawnMultiPlanarFormats, FenceFeature};
}
static std::string MakeLabel(const wgpu::SharedTextureMemoryDmaBufDescriptor& desc) {
// Internally, the GBM enums are defined as their fourcc values. Cast to that and use
// it as the label. The fourcc value is a four-character name that can be
// interpreted as a 32-bit integer enum ('ABGR', 'r011', etc.)
return std::string(reinterpret_cast<const char*>(&desc.drmFormat), 4) + " " +
"modifier:" + std::to_string(desc.drmModifier) + " " +
std::to_string(desc.size.width) + "x" + std::to_string(desc.size.height);
}
template <typename CreateFn>
auto CreateSharedTextureMemoryHelper(uint32_t size,
uint32_t format,
uint32_t usage,
CreateFn createFn) {
gbm_bo* bo = gbm_bo_create(mGbmDevice, size, size, format, usage);
EXPECT_NE(bo, nullptr) << "Failed to create GBM buffer object";
wgpu::SharedTextureMemoryDmaBufDescriptor dmaBufDesc;
dmaBufDesc.size = {size, size};
dmaBufDesc.drmFormat = format;
dmaBufDesc.drmModifier = gbm_bo_get_modifier(bo);
wgpu::SharedTextureMemoryDmaBufPlane planes[GBM_MAX_PLANES];
dmaBufDesc.planeCount = gbm_bo_get_plane_count(bo);
dmaBufDesc.planes = planes;
DAWN_ASSERT(dmaBufDesc.planeCount <= GBM_MAX_PLANES);
for (uint32_t plane = 0; plane < dmaBufDesc.planeCount; ++plane) {
planes[plane].fd = gbm_bo_get_fd(bo);
planes[plane].stride = gbm_bo_get_stride_for_plane(bo, plane);
planes[plane].offset = gbm_bo_get_offset(bo, plane);
}
std::string label = MakeLabel(dmaBufDesc);
wgpu::SharedTextureMemoryDescriptor desc;
desc.label = label.c_str();
desc.nextInChain = &dmaBufDesc;
auto ret = createFn(desc);
for (uint32_t plane = 0; plane < dmaBufDesc.planeCount; ++plane) {
close(planes[plane].fd);
}
gbm_bo_destroy(bo);
return ret;
}
// Create one basic shared texture memory. It should support most operations.
wgpu::SharedTextureMemory CreateSharedTextureMemory(const wgpu::Device& device) override {
auto format = GBM_FORMAT_ABGR8888;
auto usage = GBM_BO_USE_LINEAR;
DAWN_ASSERT(gbm_device_is_format_supported(mGbmDevice, format, usage));
return CreateSharedTextureMemoryHelper(
16, format, usage, [&](const wgpu::SharedTextureMemoryDescriptor& desc) {
return device.ImportSharedTextureMemory(&desc);
});
}
std::vector<std::vector<wgpu::SharedTextureMemory>> CreatePerDeviceSharedTextureMemories(
const std::vector<wgpu::Device>& devices) override {
std::vector<std::vector<wgpu::SharedTextureMemory>> memories;
for (uint32_t format : {
GBM_FORMAT_R8,
GBM_FORMAT_GR88,
GBM_FORMAT_ABGR8888,
GBM_FORMAT_ARGB8888,
GBM_FORMAT_XBGR8888,
GBM_FORMAT_XRGB8888,
GBM_FORMAT_ABGR2101010,
GBM_FORMAT_NV12,
}) {
for (gbm_bo_flags usage : {
gbm_bo_flags(0),
GBM_BO_USE_LINEAR,
GBM_BO_USE_RENDERING,
gbm_bo_flags(GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR),
}) {
if (format == GBM_FORMAT_NV12 && (usage & GBM_BO_USE_LINEAR)) {
// TODO(crbug.com/dawn/1548): Linear multiplanar formats require disjoint
// planes which are not supported yet.
continue;
}
if (!gbm_device_is_format_supported(mGbmDevice, format, usage)) {
continue;
}
for (uint32_t size : {4, 64}) {
CreateSharedTextureMemoryHelper(
size, format, usage, [&](const wgpu::SharedTextureMemoryDescriptor& desc) {
std::vector<wgpu::SharedTextureMemory> perDeviceMemories;
for (auto& device : devices) {
perDeviceMemories.push_back(
device.ImportSharedTextureMemory(&desc));
}
memories.push_back(std::move(perDeviceMemories));
return true;
});
}
}
}
return memories;
}
wgpu::SharedFence ImportFenceTo(const wgpu::Device& importingDevice,
const wgpu::SharedFence& fence) override {
wgpu::SharedFenceExportInfo exportInfo;
fence.ExportInfo(&exportInfo);
switch (exportInfo.type) {
case wgpu::SharedFenceType::VkSemaphoreOpaqueFD: {
wgpu::SharedFenceVkSemaphoreOpaqueFDExportInfo vkExportInfo;
exportInfo.nextInChain = &vkExportInfo;
fence.ExportInfo(&exportInfo);
wgpu::SharedFenceVkSemaphoreOpaqueFDDescriptor vkDesc;
vkDesc.handle = vkExportInfo.handle;
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &vkDesc;
return importingDevice.ImportSharedFence(&fenceDesc);
}
case wgpu::SharedFenceType::VkSemaphoreSyncFD: {
wgpu::SharedFenceVkSemaphoreSyncFDExportInfo vkExportInfo;
exportInfo.nextInChain = &vkExportInfo;
fence.ExportInfo(&exportInfo);
wgpu::SharedFenceVkSemaphoreSyncFDDescriptor vkDesc;
vkDesc.handle = vkExportInfo.handle;
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &vkDesc;
return importingDevice.ImportSharedFence(&fenceDesc);
}
case wgpu::SharedFenceType::VkSemaphoreZirconHandle: {
wgpu::SharedFenceVkSemaphoreZirconHandleExportInfo vkExportInfo;
exportInfo.nextInChain = &vkExportInfo;
fence.ExportInfo(&exportInfo);
wgpu::SharedFenceVkSemaphoreZirconHandleDescriptor vkDesc;
vkDesc.handle = vkExportInfo.handle;
wgpu::SharedFenceDescriptor fenceDesc;
fenceDesc.nextInChain = &vkDesc;
return importingDevice.ImportSharedFence(&fenceDesc);
}
default:
DAWN_UNREACHABLE();
}
}
struct BackendBeginStateVk : public BackendBeginState {
wgpu::SharedTextureMemoryVkImageLayoutBeginState imageLayouts{};
};
struct BackendEndStateVk : public BackendEndState {
wgpu::SharedTextureMemoryVkImageLayoutEndState imageLayouts{};
};
std::unique_ptr<BackendBeginState> ChainInitialBeginState(
wgpu::SharedTextureMemoryBeginAccessDescriptor* beginDesc) override {
auto state = std::make_unique<BackendBeginStateVk>();
state->imageLayouts.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
state->imageLayouts.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
beginDesc->nextInChain = &state->imageLayouts;
return state;
}
std::unique_ptr<BackendEndState> ChainEndState(
wgpu::SharedTextureMemoryEndAccessState* endState) override {
auto state = std::make_unique<BackendEndStateVk>();
endState->nextInChain = &state->imageLayouts;
return state;
}
std::unique_ptr<BackendBeginState> ChainBeginState(
wgpu::SharedTextureMemoryBeginAccessDescriptor* beginDesc,
const wgpu::SharedTextureMemoryEndAccessState& endState) override {
DAWN_ASSERT(endState.nextInChain != nullptr);
DAWN_ASSERT(endState.nextInChain->sType ==
wgpu::SType::SharedTextureMemoryVkImageLayoutEndState);
auto* vkEndState =
static_cast<wgpu::SharedTextureMemoryVkImageLayoutEndState*>(endState.nextInChain);
auto state = std::make_unique<BackendBeginStateVk>();
state->imageLayouts.oldLayout = vkEndState->oldLayout;
state->imageLayouts.newLayout = vkEndState->newLayout;
beginDesc->nextInChain = &state->imageLayouts;
return state;
}
private:
void SetUp() override {
// Render nodes [1] are the primary interface for communicating with the GPU on
// devices that support DRM. The actual filename of the render node is
// implementation-specific, so we must scan through all possible filenames to find
// one that we can use [2].
//
// [1] https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#render-nodes
// [2]
// https://cs.chromium.org/chromium/src/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc
const uint32_t kRenderNodeStart = 128;
const uint32_t kRenderNodeEnd = kRenderNodeStart + 16;
const std::string kRenderNodeTemplate = "/dev/dri/renderD";
for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) {
std::string renderNode = kRenderNodeTemplate + std::to_string(i);
mRenderNodeFd = open(renderNode.c_str(), O_RDWR);
if (mRenderNodeFd >= 0) {
break;
}
}
// Failed to get file descriptor for render node and mGbmDevice is nullptr.
DAWN_TEST_UNSUPPORTED_IF(mRenderNodeFd < 0);
mGbmDevice = gbm_create_device(mRenderNodeFd);
DAWN_TEST_UNSUPPORTED_IF(mGbmDevice == nullptr);
// Make sure we can successfully create a basic buffer object.
gbm_bo* bo = gbm_bo_create(mGbmDevice, 16, 16, GBM_FORMAT_XBGR8888, GBM_BO_USE_LINEAR);
if (bo != nullptr) {
gbm_bo_destroy(bo);
}
DAWN_TEST_UNSUPPORTED_IF(bo == nullptr);
}
void TearDown() override {
if (mGbmDevice) {
gbm_device_destroy(mGbmDevice);
mGbmDevice = nullptr;
}
if (mRenderNodeFd >= 0) {
close(mRenderNodeFd);
}
}
int mRenderNodeFd = -1;
gbm_device* mGbmDevice = nullptr;
};
DAWN_INSTANTIATE_PREFIXED_TEST_P(
Vulkan,
SharedTextureMemoryNoFeatureTests,
{VulkanBackend()},
{Backend<wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD>::GetInstance(),
Backend<wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD>::GetInstance()});
DAWN_INSTANTIATE_PREFIXED_TEST_P(
Vulkan,
SharedTextureMemoryTests,
{VulkanBackend()},
{Backend<wgpu::FeatureName::SharedFenceVkSemaphoreOpaqueFD>::GetInstance(),
Backend<wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD>::GetInstance()});
} // anonymous namespace
} // namespace dawn