blob: ddbae1671bd6505979038b19230f8cf13606d6af [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
*/
/*
* Copyright (c) 2020, Alliance for Open Media. All rights reserved
*
* This source code is subject to the terms of the BSD 2 Clause License and
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
* was not distributed with this source code in the LICENSE file, you can
* obtain it at www.aomedia.org/license/software. If the Alliance for Open
* Media Patent License 1.0 was not distributed with this source code in the
* PATENTS file, you can obtain it at www.aomedia.org/license/patent.
*/
#include "frame_outs.h"
#include <assert.h>
#include "d3dx12.h"
#if defined(_DEBUG)
inline void SetName(ID3D12Object* pObject, LPCWSTR name) { pObject->SetName(name); }
inline void SetNameIndexed(ID3D12Object* pObject, LPCWSTR name, UINT index) {
WCHAR fullName[50];
if (swprintf_s(fullName, L"%s[%u]", name, index) > 0) {
pObject->SetName(fullName);
}
}
#define NAME_D3D12_OBJECT(x) SetName(x.Get(), L#x)
#define NAME_D3D12_OBJECT_INDEXED(x, n) SetNameIndexed(x[n].Get(), L#x, n)
#else
#define NAME_D3D12_OBJECT(x)
#define NAME_D3D12_OBJECT_INDEXED(x, n)
#endif
OutBufferWrapper::~OutBufferWrapper() {
if (storage_ && buffer_) {
XB_LOGD << "Release OutBuffer " << buffer_;
storage_->Push(buffer_);
}
}
void OutBufferWrapper::ReleaseRented() {
if (storage_ && buffer_) {
storage_->ReleaseRentedBuffer(buffer_);
}
}
OutBufferStorage::OutBufferStorage(int queue_size, ID3D12Device* dx12_device)
: storage_(queue_size), queue_free_(queue_size), queue_size_(queue_size), dx12_device_(dx12_device) {
for (int i = 0; i < queue_size_; i++) {
OutBuffer& item = storage_[i];
item.shown = 0;
queue_free_.Push(&item);
}
}
void OutBufferStorage::Push(OutBuffer* frame) { queue_free_.Push(frame); }
std::shared_ptr<OutBufferWrapper> OutBufferStorage::GetFreeBuffer(uint32_t width, uint32_t height,
frame_buffer_type fb_type, size_t buffer_size,
uint32_t flags) {
HRESULT hr;
int has_errors = 0;
queue_free_.DoForAll(1, [&](OutBuffer* buffer) {
if (flags & CBUFTEXTURE) {
if (buffer->renderWidth != width || buffer->renderHeight != height || buffer->fb_type != fb_type) {
for (int i = 0; i < 3; i++) {
const int subsampling = i > 0;
const int _width = (fb_type == fbt10x3) ? (((width + subsampling) >> subsampling) + 2) / 3
: ((width + subsampling) >> subsampling);
const int _height = (height + subsampling) >> subsampling;
// Create interop resources
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.MipLevels = 1;
textureDesc.Format = (fb_type == fbt10bit)
? DXGI_FORMAT_R16_UNORM
: ((fb_type == fbt8bit) ? DXGI_FORMAT_R8_UNORM : DXGI_FORMAT_R10G10B10A2_UNORM);
textureDesc.Width = _width;
textureDesc.Height = _height;
textureDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
textureDesc.DepthOrArraySize = 1;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
hr = dx12_device_->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_SHARED, &textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST, 0,
IID_PPV_ARGS(&buffer->d3d12textures[i]));
if (FAILED(hr)) {
has_errors = 1;
return 0;
}
NAME_D3D12_OBJECT_INDEXED(buffer->d3d12textures, i);
}
buffer->renderWidth = width;
buffer->renderHeight = height;
buffer->fb_type = fb_type;
}
}
if (flags & CBUFREADBACK) {
if (buffer->buffer_size < buffer_size) {
// Create readback buffer (if any)
const D3D12_HEAP_TYPE heapType = D3D12_HEAP_TYPE_READBACK;
const D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_COPY_DEST;
D3D12_HEAP_PROPERTIES heapProp;
heapProp.Type = heapType;
heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; //
heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProp.CreationNodeMask = 0;
heapProp.VisibleNodeMask = 0;
const D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE;
hr = dx12_device_->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(buffer_size, flags), state, NULL,
IID_PPV_ARGS(&buffer->d3d12buffer));
if (SUCCEEDED(hr)) {
hr = buffer->d3d12buffer->Map(0, NULL, &buffer->pMem);
if (FAILED(hr)) {
has_errors = 1;
return 0;
}
buffer->d3d12buffer->Unmap(0, 0);
}
buffer->buffer_size = buffer_size;
}
}
return 1;
});
if (has_errors) {
XB_LOGE << "Failed to allocate output buffers.";
queue_free_.DoForAll(1, [&](OutBuffer* buffer) {
buffer->Reset();
return 1;
});
return 0;
}
OutBuffer* buffer = queue_free_.Pull();
if (!(flags & CBUFREADBACK)) {
buffer->d3d12buffer.Reset();
buffer->pMem = 0;
}
std::unique_lock<std::mutex> lock(cs_);
OutBufferWrapper* bf = new (std::nothrow) OutBufferWrapper(buffer, this);
if (!bf) return 0;
std::shared_ptr<OutBufferWrapper> retbuf = std::shared_ptr<OutBufferWrapper>(bf);
rented_.push_back(retbuf);
return retbuf;
}
std::shared_ptr<OutBufferWrapper> OutBufferStorage::ReleaseRentedBuffer(void* ptr) {
std::unique_lock<std::mutex> lock(cs_);
for (auto iter = rented_.begin(); iter != rented_.end(); iter++) {
if ((*iter)->Buffer() == ptr) {
XB_LOGD << "ReleaseRentedBuffer " << ptr;
std::shared_ptr<OutBufferWrapper> buff = *iter;
rented_.erase(iter);
return buff;
}
}
assert(0 && "buffer not found");
return 0;
}
std::shared_ptr<OutBufferWrapper> OutBufferStorage::FindRentedBuffer(void* ptr) {
std::unique_lock<std::mutex> lock(cs_);
for (auto iter = rented_.begin(); iter != rented_.end(); iter++) {
if ((*iter)->Buffer() == ptr) {
std::shared_ptr<OutBufferWrapper> buff = *iter;
return buff;
}
}
assert(0 && "buffer not found");
return 0;
}