blob: 960d85d653ad1b6d368cb1adcb316f90131e33e7 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/windows/d3d11_picture_buffer.h"
#include <d3d11.h>
#include <d3d11_1.h>
#include <windows.h>
#include <memory>
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "media/gpu/windows/return_on_failure.h"
#include "third_party/angle/include/EGL/egl.h"
#include "third_party/angle/include/EGL/eglext.h"
#include "ui/gfx/color_space.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_image_dxgi.h"
#include "ui/gl/gl_surface_egl.h"
#include "ui/gl/scoped_binders.h"
namespace media {
D3D11PictureBuffer::D3D11PictureBuffer(GLenum target,
gfx::Size size,
size_t level)
: target_(target), size_(size), level_(level) {}
D3D11PictureBuffer::~D3D11PictureBuffer() {
// TODO(liberato): post destruction of |gpu_resources_| to the gpu thread.
}
bool D3D11PictureBuffer::Init(
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb,
Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device,
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture,
const GUID& decoder_guid,
int textures_per_picture) {
texture_ = texture;
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = {};
view_desc.DecodeProfile = decoder_guid;
view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
view_desc.Texture2D.ArraySlice = (UINT)level_;
HRESULT hr = video_device->CreateVideoDecoderOutputView(
texture.Get(), &view_desc, output_view_.GetAddressOf());
if (!SUCCEEDED(hr))
return false;
// Generate mailboxes and holders.
std::vector<gpu::Mailbox> mailboxes;
for (int texture_idx = 0; texture_idx < textures_per_picture; texture_idx++) {
mailboxes.push_back(gpu::Mailbox::Generate());
mailbox_holders_[texture_idx] = gpu::MailboxHolder(
mailboxes[texture_idx], gpu::SyncToken(), GL_TEXTURE_EXTERNAL_OES);
}
// Start construction of the GpuResources.
// We send the texture itself, since we assume that we're using the angle
// device for decoding. Sharing seems not to work very well. Otherwise, we
// would create the texture with KEYED_MUTEX and NTHANDLE, then send along
// a handle that we get from |texture| as an IDXGIResource1.
// TODO(liberato): this should happen on the gpu thread.
gpu_resources_ = std::make_unique<GpuResources>();
if (!gpu_resources_->Init(std::move(get_helper_cb), level_,
std::move(mailboxes), target_, size_, texture,
textures_per_picture))
return false;
return true;
}
D3D11PictureBuffer::GpuResources::GpuResources() {}
D3D11PictureBuffer::GpuResources::~GpuResources() {
if (helper_ && helper_->MakeContextCurrent()) {
for (uint32_t service_id : service_ids_)
helper_->DestroyTexture(service_id);
}
}
bool D3D11PictureBuffer::GpuResources::Init(
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb,
int level,
const std::vector<gpu::Mailbox> mailboxes,
GLenum target,
gfx::Size size,
Microsoft::WRL::ComPtr<ID3D11Texture2D> angle_texture,
int textures_per_picture) {
helper_ = get_helper_cb.Run();
if (!helper_ || !helper_->MakeContextCurrent())
return false;
// Create the textures and attach them to the mailboxes.
for (int texture_idx = 0; texture_idx < textures_per_picture; texture_idx++) {
uint32_t service_id =
helper_->CreateTexture(target, GL_RGBA, size.width(), size.height(),
GL_RGBA, GL_UNSIGNED_BYTE);
service_ids_.push_back(service_id);
helper_->ProduceTexture(mailboxes[texture_idx], service_id);
}
// Create the stream for zero-copy use by gl.
EGLDisplay egl_display = gl::GLSurfaceEGL::GetHardwareDisplay();
const EGLint stream_attributes[] = {
EGL_CONSUMER_LATENCY_USEC_KHR,
0,
EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR,
0,
EGL_NONE,
};
EGLStreamKHR stream = eglCreateStreamKHR(egl_display, stream_attributes);
RETURN_ON_FAILURE(!!stream, "Could not create stream", false);
// |stream| will be destroyed when the GLImage is.
// TODO(liberato): for tests, it will be destroyed pretty much at the end of
// this function unless |helper_| retains it. Also, this won't work if we
// have a FakeCommandBufferHelper since the service IDs aren't meaningful.
scoped_refptr<gl::GLImage> gl_image =
base::MakeRefCounted<gl::GLImageDXGI>(size, stream);
gl::ScopedActiveTexture texture0(GL_TEXTURE0);
gl::ScopedTextureBinder texture0_binder(GL_TEXTURE_EXTERNAL_OES,
service_ids_[0]);
gl::ScopedActiveTexture texture1(GL_TEXTURE1);
gl::ScopedTextureBinder texture1_binder(GL_TEXTURE_EXTERNAL_OES,
service_ids_[1]);
EGLAttrib consumer_attributes[] = {
EGL_COLOR_BUFFER_TYPE,
EGL_YUV_BUFFER_EXT,
EGL_YUV_NUMBER_OF_PLANES_EXT,
2,
EGL_YUV_PLANE0_TEXTURE_UNIT_NV,
0,
EGL_YUV_PLANE1_TEXTURE_UNIT_NV,
1,
EGL_NONE,
};
EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
egl_display, stream, consumer_attributes);
RETURN_ON_FAILURE(result, "Could not set stream consumer", false);
EGLAttrib producer_attributes[] = {
EGL_NONE,
};
result = eglCreateStreamProducerD3DTextureANGLE(egl_display, stream,
producer_attributes);
RETURN_ON_FAILURE(result, "Could not create stream", false);
EGLAttrib frame_attributes[] = {
EGL_D3D_TEXTURE_SUBRESOURCE_ID_ANGLE, level, EGL_NONE,
};
result = eglStreamPostD3DTextureANGLE(egl_display, stream,
static_cast<void*>(angle_texture.Get()),
frame_attributes);
RETURN_ON_FAILURE(result, "Could not post texture", false);
result = eglStreamConsumerAcquireKHR(egl_display, stream);
RETURN_ON_FAILURE(result, "Could not post acquire stream", false);
gl::GLImageDXGI* gl_image_dxgi =
static_cast<gl::GLImageDXGI*>(gl_image.get());
gl_image_dxgi->SetTexture(angle_texture, level);
// Bind the image to each texture.
for (size_t texture_idx = 0; texture_idx < service_ids_.size();
texture_idx++) {
helper_->BindImage(service_ids_[texture_idx], gl_image.get(),
false /* client_managed */);
}
return true;
}
} // namespace media