| // Copyright 2021 The Dawn Authors | |
| // | |
| // 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 "VideoViewsTests.h" | |
| #include "common/Assert.h" | |
| #include "dawn_native/VulkanBackend.h" | |
| #include <fcntl.h> | |
| #include <gbm.h> | |
| // "linux-chromeos-rel"'s gbm.h is too old to compile, missing this change at least: | |
| // https://chromium-review.googlesource.com/c/chromiumos/platform/minigbm/+/1963001/10/gbm.h#244 | |
| #ifndef MINIGBM | |
| # define GBM_BO_USE_TEXTURING (1 << 5) | |
| # define GBM_BO_USE_SW_WRITE_RARELY (1 << 12) | |
| # define GBM_BO_USE_HW_VIDEO_DECODER (1 << 13) | |
| #endif | |
| class PlatformTextureGbm : public VideoViewsTestBackend::PlatformTexture { | |
| public: | |
| PlatformTextureGbm(wgpu::Texture&& texture, gbm_bo* gbmBo) | |
| : PlatformTexture(std::move(texture)), mGbmBo(gbmBo) { | |
| } | |
| ~PlatformTextureGbm() override = default; | |
| // TODO(chromium:1258986): Add DISJOINT vkImage support for multi-plannar formats. | |
| bool CanWrapAsWGPUTexture() override { | |
| ASSERT(mGbmBo != nullptr); | |
| // Checks if all plane handles of a multi-planar gbm_bo are same. | |
| gbm_bo_handle plane0Handle = gbm_bo_get_handle_for_plane(mGbmBo, 0); | |
| for (int plane = 1; plane < gbm_bo_get_plane_count(mGbmBo); ++plane) { | |
| if (gbm_bo_get_handle_for_plane(mGbmBo, plane).u32 != plane0Handle.u32) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| gbm_bo* GetGbmBo() { | |
| return mGbmBo; | |
| } | |
| private: | |
| gbm_bo* mGbmBo = nullptr; | |
| }; | |
| class VideoViewsTestBackendGbm : public VideoViewsTestBackend { | |
| public: | |
| void OnSetUp(WGPUDevice device) override { | |
| mWGPUDevice = device; | |
| mGbmDevice = CreateGbmDevice(); | |
| } | |
| void OnTearDown() override { | |
| gbm_device_destroy(mGbmDevice); | |
| } | |
| private: | |
| gbm_device* CreateGbmDevice() { | |
| // 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"; | |
| int renderNodeFd = -1; | |
| for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) { | |
| std::string renderNode = kRenderNodeTemplate + std::to_string(i); | |
| renderNodeFd = open(renderNode.c_str(), O_RDWR); | |
| if (renderNodeFd >= 0) | |
| break; | |
| } | |
| ASSERT(renderNodeFd > 0); | |
| gbm_device* gbmDevice = gbm_create_device(renderNodeFd); | |
| ASSERT(gbmDevice != nullptr); | |
| return gbmDevice; | |
| } | |
| static uint32_t GetGbmBoFormat(wgpu::TextureFormat format) { | |
| switch (format) { | |
| case wgpu::TextureFormat::R8BG8Biplanar420Unorm: | |
| return GBM_FORMAT_NV12; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| WGPUTextureFormat ToWGPUTextureFormat(wgpu::TextureFormat format) { | |
| switch (format) { | |
| case wgpu::TextureFormat::R8BG8Biplanar420Unorm: | |
| return WGPUTextureFormat_R8BG8Biplanar420Unorm; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| WGPUTextureUsage ToWGPUTextureUsage(wgpu::TextureUsage usage) { | |
| switch (usage) { | |
| case wgpu::TextureUsage::TextureBinding: | |
| return WGPUTextureUsage_TextureBinding; | |
| default: | |
| UNREACHABLE(); | |
| } | |
| } | |
| std::unique_ptr<VideoViewsTestBackend::PlatformTexture> CreateVideoTextureForTest( | |
| wgpu::TextureFormat format, | |
| wgpu::TextureUsage usage, | |
| bool isCheckerboard) override { | |
| uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_TEXTURING | GBM_BO_USE_HW_VIDEO_DECODER | | |
| GBM_BO_USE_SW_WRITE_RARELY; | |
| gbm_bo* gbmBo = gbm_bo_create(mGbmDevice, VideoViewsTests::kYUVImageDataWidthInTexels, | |
| VideoViewsTests::kYUVImageDataHeightInTexels, | |
| GetGbmBoFormat(format), flags); | |
| if (gbmBo == nullptr) { | |
| return nullptr; | |
| } | |
| void* mapHandle = nullptr; | |
| uint32_t strideBytes = 0; | |
| void* addr = gbm_bo_map(gbmBo, 0, 0, VideoViewsTests::kYUVImageDataWidthInTexels, | |
| VideoViewsTests::kYUVImageDataHeightInTexels, GBM_BO_TRANSFER_WRITE, | |
| &strideBytes, &mapHandle); | |
| EXPECT_NE(addr, nullptr); | |
| std::vector<uint8_t> initialData = | |
| VideoViewsTests::GetTestTextureData(format, isCheckerboard); | |
| std::memcpy(addr, initialData.data(), initialData.size()); | |
| gbm_bo_unmap(gbmBo, mapHandle); | |
| wgpu::TextureDescriptor textureDesc; | |
| textureDesc.format = format; | |
| textureDesc.dimension = wgpu::TextureDimension::e2D; | |
| textureDesc.usage = usage; | |
| textureDesc.size = {VideoViewsTests::kYUVImageDataWidthInTexels, | |
| VideoViewsTests::kYUVImageDataHeightInTexels, 1}; | |
| wgpu::DawnTextureInternalUsageDescriptor internalDesc; | |
| internalDesc.internalUsage = wgpu::TextureUsage::CopySrc; | |
| textureDesc.nextInChain = &internalDesc; | |
| dawn_native::vulkan::ExternalImageDescriptorDmaBuf descriptor = {}; | |
| descriptor.cTextureDescriptor = | |
| reinterpret_cast<const WGPUTextureDescriptor*>(&textureDesc); | |
| descriptor.isInitialized = true; | |
| descriptor.memoryFD = gbm_bo_get_fd(gbmBo); | |
| descriptor.stride = gbm_bo_get_stride(gbmBo); | |
| descriptor.drmModifier = gbm_bo_get_modifier(gbmBo); | |
| descriptor.waitFDs = {}; | |
| return std::make_unique<PlatformTextureGbm>( | |
| wgpu::Texture::Acquire(dawn_native::vulkan::WrapVulkanImage(mWGPUDevice, &descriptor)), | |
| gbmBo); | |
| } | |
| void DestroyVideoTextureForTest( | |
| std::unique_ptr<VideoViewsTestBackend::PlatformTexture>&& platformTexture) override { | |
| // Exports the signal and ignores it. | |
| dawn_native::vulkan::ExternalImageExportInfoDmaBuf exportInfo; | |
| dawn_native::vulkan::ExportVulkanImage(platformTexture->wgpuTexture.Get(), | |
| VK_IMAGE_LAYOUT_GENERAL, &exportInfo); | |
| for (int fd : exportInfo.semaphoreHandles) { | |
| ASSERT_NE(fd, -1); | |
| close(fd); | |
| } | |
| gbm_bo* gbmBo = static_cast<PlatformTextureGbm*>(platformTexture.get())->GetGbmBo(); | |
| ASSERT_NE(gbmBo, nullptr); | |
| gbm_bo_destroy(gbmBo); | |
| } | |
| WGPUDevice mWGPUDevice = nullptr; | |
| gbm_device* mGbmDevice = nullptr; | |
| }; | |
| // static | |
| BackendTestConfig VideoViewsTestBackend::Backend() { | |
| return VulkanBackend(); | |
| } | |
| // static | |
| std::unique_ptr<VideoViewsTestBackend> VideoViewsTestBackend::Create() { | |
| return std::make_unique<VideoViewsTestBackendGbm>(); | |
| } |