зеркало из https://github.com/mozilla/gecko-dev.git
381 строка
10 KiB
C++
381 строка
10 KiB
C++
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "MLGDevice.h"
|
|
#include "mozilla/layers/TextureHost.h"
|
|
#include "BufferCache.h"
|
|
#include "ClearRegionHelper.h"
|
|
#include "gfxConfig.h"
|
|
#include "gfxPrefs.h"
|
|
#include "gfxUtils.h"
|
|
#include "ShaderDefinitionsMLGPU.h"
|
|
#include "SharedBufferMLGPU.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
using namespace gfx;
|
|
using namespace mlg;
|
|
|
|
MLGRenderTarget::MLGRenderTarget(MLGRenderTargetFlags aFlags)
|
|
: mFlags(aFlags),
|
|
mLastDepthStart(-1)
|
|
{
|
|
}
|
|
|
|
MLGSwapChain::MLGSwapChain()
|
|
: mIsDoubleBuffered(false)
|
|
{
|
|
}
|
|
|
|
bool
|
|
MLGSwapChain::ApplyNewInvalidRegion(nsIntRegion&& aRegion, const Maybe<gfx::IntRect>& aExtraRect)
|
|
{
|
|
// We clamp the invalid region to the backbuffer size, otherwise the present
|
|
// can fail.
|
|
IntRect bounds(IntPoint(0, 0), GetSize());
|
|
nsIntRegion invalid = Move(aRegion);
|
|
invalid.AndWith(bounds);
|
|
if (invalid.IsEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
if (aExtraRect) {
|
|
IntRect rect = aExtraRect.value().Intersect(bounds);
|
|
if (!rect.IsEmpty()) {
|
|
invalid.OrWith(rect);
|
|
}
|
|
}
|
|
|
|
// This area is now invalid in the back and front buffers. Note that the front
|
|
// buffer is either totally valid or totally invalid, since either the last
|
|
// paint succeeded or was thrown out due to a buffer resize. Effectively, it
|
|
// will now contain the invalid region specific to this frame.
|
|
mBackBufferInvalid.OrWith(invalid);
|
|
AL_LOG("Backbuffer invalid region: %s\n", Stringify(mBackBufferInvalid).c_str());
|
|
|
|
if (mIsDoubleBuffered) {
|
|
mFrontBufferInvalid.OrWith(invalid);
|
|
AL_LOG("Frontbuffer invalid region: %s\n", Stringify(mFrontBufferInvalid).c_str());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MLGDevice::MLGDevice()
|
|
: mTopology(MLGPrimitiveTopology::Unknown),
|
|
mIsValid(false),
|
|
mCanUseClearView(false),
|
|
mCanUseConstantBufferOffsetBinding(false),
|
|
mMaxConstantBufferBindSize(0)
|
|
{
|
|
}
|
|
|
|
MLGDevice::~MLGDevice()
|
|
{
|
|
}
|
|
|
|
bool
|
|
MLGDevice::Initialize()
|
|
{
|
|
if (!mMaxConstantBufferBindSize) {
|
|
return Fail("FEATURE_FAILURE_NO_MAX_CB_BIND_SIZE", "Failed to set a max constant buffer bind size");
|
|
}
|
|
if (mMaxConstantBufferBindSize < mlg::kMaxConstantBufferSize) {
|
|
// StagingBuffer depends on this value being accurate, so for now we just
|
|
// double-check it here.
|
|
return Fail("FEATURE_FAILURE_MIN_MAX_CB_BIND_SIZE", "Minimum constant buffer bind size not met");
|
|
}
|
|
|
|
// We allow this to be pref'd off for testing. Switching it off enables
|
|
// Direct3D 11.0/Windows 7/OpenGL-style buffer code paths.
|
|
if (!gfxPrefs::AdvancedLayersEnableBufferSharing()) {
|
|
gfxConfig::EnableFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING,
|
|
"Disabled by pref");
|
|
mCanUseConstantBufferOffsetBinding = false;
|
|
}
|
|
if (mCanUseConstantBufferOffsetBinding && !VerifyConstantBufferOffsetting()) {
|
|
gfxConfig::EnableFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING,
|
|
"Constant buffer offset binding does not work");
|
|
mCanUseConstantBufferOffsetBinding = false;
|
|
}
|
|
|
|
// We allow this to be pref'd off for testing. Disabling it turns on
|
|
// ID3D11DeviceContext1::ClearView support, which is present on
|
|
// newer Windows 8+ drivers.
|
|
if (!gfxPrefs::AdvancedLayersEnableClearView()) {
|
|
mCanUseClearView = false;
|
|
}
|
|
|
|
// When compositing normal sized layer trees, we typically have small vertex
|
|
// buffers. Empirically the vertex and pixel constant buffer sizes are generally
|
|
// under 1KB and the vertex constant buffer size is under 8KB.
|
|
static const size_t kDefaultVertexBufferSize = 4096;
|
|
static const size_t kDefaultVSConstantBufferSize = 512 * kConstantBufferElementSize;
|
|
static const size_t kDefaultPSConstantBufferSize = 256 * kConstantBufferElementSize;
|
|
|
|
// Note: we create these after we've verified all the device-specific properties above.
|
|
mSharedVertexBuffer = MakeUnique<SharedVertexBuffer>(this, kDefaultVertexBufferSize);
|
|
mSharedVSBuffer = MakeUnique<SharedConstantBuffer>(this, kDefaultVSConstantBufferSize);
|
|
mSharedPSBuffer = MakeUnique<SharedConstantBuffer>(this, kDefaultPSConstantBufferSize);
|
|
|
|
if (!mSharedVertexBuffer->Init() ||
|
|
!mSharedVSBuffer->Init() ||
|
|
!mSharedPSBuffer->Init())
|
|
{
|
|
return Fail("FEATURE_FAILURE_ALLOC_SHARED_BUFFER", "Failed to allocate a shared shader buffer");
|
|
}
|
|
|
|
if (gfxPrefs::AdvancedLayersEnableBufferCache()) {
|
|
mConstantBufferCache = MakeUnique<BufferCache>(this);
|
|
}
|
|
|
|
mInitialized = true;
|
|
mIsValid = true;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MLGDevice::BeginFrame()
|
|
{
|
|
mSharedVertexBuffer->Reset();
|
|
mSharedPSBuffer->Reset();
|
|
mSharedVSBuffer->Reset();
|
|
}
|
|
|
|
void
|
|
MLGDevice::EndFrame()
|
|
{
|
|
if (mConstantBufferCache) {
|
|
mConstantBufferCache->EndFrame();
|
|
}
|
|
}
|
|
|
|
void
|
|
MLGDevice::FinishSharedBufferUse()
|
|
{
|
|
mSharedVertexBuffer->PrepareForUsage();
|
|
mSharedPSBuffer->PrepareForUsage();
|
|
mSharedVSBuffer->PrepareForUsage();
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetTopology(MLGPrimitiveTopology aTopology)
|
|
{
|
|
if (mTopology == aTopology) {
|
|
return;
|
|
}
|
|
SetPrimitiveTopology(aTopology);
|
|
mTopology = aTopology;
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetVertexBuffer(uint32_t aSlot, const VertexBufferSection* aSection)
|
|
{
|
|
if (!aSection->IsValid()) {
|
|
return;
|
|
}
|
|
SetVertexBuffer(aSlot, aSection->GetBuffer(), aSection->Stride(), aSection->Offset());
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetPSConstantBuffer(uint32_t aSlot, const ConstantBufferSection* aSection)
|
|
{
|
|
if (!aSection->IsValid()) {
|
|
return;
|
|
}
|
|
|
|
MLGBuffer* buffer = aSection->GetBuffer();
|
|
|
|
if (aSection->HasOffset()) {
|
|
uint32_t first = aSection->Offset();
|
|
uint32_t numConstants = aSection->NumConstants();
|
|
SetPSConstantBuffer(aSlot, buffer, first, numConstants);
|
|
} else {
|
|
SetPSConstantBuffer(aSlot, buffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetVSConstantBuffer(uint32_t aSlot, const ConstantBufferSection* aSection)
|
|
{
|
|
if (!aSection->IsValid()) {
|
|
return;
|
|
}
|
|
|
|
MLGBuffer* buffer = aSection->GetBuffer();
|
|
|
|
if (aSection->HasOffset()) {
|
|
uint32_t first = aSection->Offset();
|
|
uint32_t numConstants = aSection->NumConstants();
|
|
SetVSConstantBuffer(aSlot, buffer, first, numConstants);
|
|
} else {
|
|
SetVSConstantBuffer(aSlot, buffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetPSTexturesYUV(uint32_t aSlot, TextureSource* aTexture)
|
|
{
|
|
// Note, we don't support tiled YCbCr textures.
|
|
const int Y = 0, Cb = 1, Cr = 2;
|
|
TextureSource* textures[3] = {
|
|
aTexture->GetSubSource(Y),
|
|
aTexture->GetSubSource(Cb),
|
|
aTexture->GetSubSource(Cr)
|
|
};
|
|
MOZ_ASSERT(textures[0]);
|
|
MOZ_ASSERT(textures[1]);
|
|
MOZ_ASSERT(textures[2]);
|
|
|
|
SetPSTextures(0, 3, textures);
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetPSTexture(uint32_t aSlot, TextureSource* aSource)
|
|
{
|
|
SetPSTextures(aSlot, 1, &aSource);
|
|
}
|
|
|
|
void
|
|
MLGDevice::SetSamplerMode(uint32_t aIndex, gfx::SamplingFilter aFilter)
|
|
{
|
|
SetSamplerMode(aIndex, FilterToSamplerMode(aFilter));
|
|
}
|
|
|
|
bool
|
|
MLGDevice::Fail(const nsCString& aFailureId, const nsCString* aMessage)
|
|
{
|
|
const char* message = aMessage
|
|
? aMessage->get()
|
|
: "Failed initializing MLGDeviceD3D11";
|
|
gfxWarning() << "Failure initializing MLGDeviceD3D11: " << message;
|
|
mFailureId = aFailureId;
|
|
mFailureMessage = message;
|
|
return false;
|
|
}
|
|
|
|
void
|
|
MLGDevice::UnmapSharedBuffers()
|
|
{
|
|
mSharedVertexBuffer->Reset();
|
|
mSharedPSBuffer->Reset();
|
|
mSharedVSBuffer->Reset();
|
|
}
|
|
|
|
RefPtr<MLGBuffer>
|
|
MLGDevice::GetBufferForColorSpace(YUVColorSpace aColorSpace)
|
|
{
|
|
if (mColorSpaceBuffers[aColorSpace]) {
|
|
return mColorSpaceBuffers[aColorSpace];
|
|
}
|
|
|
|
YCbCrShaderConstants buffer;
|
|
memcpy(
|
|
&buffer.yuvColorMatrix,
|
|
gfxUtils::YuvToRgbMatrix4x3RowMajor(aColorSpace),
|
|
sizeof(buffer.yuvColorMatrix));
|
|
|
|
RefPtr<MLGBuffer> resource = CreateBuffer(
|
|
MLGBufferType::Constant,
|
|
sizeof(buffer),
|
|
MLGUsage::Immutable,
|
|
&buffer);
|
|
if (!resource) {
|
|
return nullptr;
|
|
}
|
|
|
|
mColorSpaceBuffers[aColorSpace] = resource;
|
|
return resource;
|
|
}
|
|
|
|
bool
|
|
MLGDevice::Synchronize()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
void
|
|
MLGDevice::PrepareClearRegion(ClearRegionHelper* aOut,
|
|
nsTArray<gfx::IntRect>&& aRects,
|
|
const Maybe<int32_t>& aSortIndex)
|
|
{
|
|
if (CanUseClearView() && !aSortIndex) {
|
|
aOut->mRects = Move(aRects);
|
|
return;
|
|
}
|
|
|
|
mSharedVertexBuffer->Allocate(
|
|
&aOut->mInput,
|
|
aRects.Length(),
|
|
sizeof(IntRect),
|
|
aRects.Elements());
|
|
|
|
ClearConstants consts(aSortIndex ? aSortIndex.value() : 1);
|
|
mSharedVSBuffer->Allocate(&aOut->mVSBuffer, consts);
|
|
}
|
|
|
|
void
|
|
MLGDevice::DrawClearRegion(const ClearRegionHelper& aHelper)
|
|
{
|
|
// If we've set up vertices for a shader-based clear, execute that now.
|
|
if (aHelper.mInput.IsValid()) {
|
|
SetTopology(MLGPrimitiveTopology::UnitQuad);
|
|
SetVertexShader(VertexShaderID::Clear);
|
|
SetVertexBuffer(1, &aHelper.mInput);
|
|
SetVSConstantBuffer(kClearConstantBufferSlot, &aHelper.mVSBuffer);
|
|
SetBlendState(MLGBlendState::Copy);
|
|
SetPixelShader(PixelShaderID::Clear);
|
|
DrawInstanced(4, aHelper.mInput.NumVertices(), 0, 0);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, if we have a normal rect list, we wanted to use the faster
|
|
// ClearView.
|
|
if (!aHelper.mRects.IsEmpty()) {
|
|
Color color(0.0, 0.0, 0.0, 0.0);
|
|
ClearView(mCurrentRT, color, aHelper.mRects.Elements(), aHelper.mRects.Length());
|
|
}
|
|
}
|
|
|
|
void
|
|
MLGDevice::WriteAsPNG(MLGTexture* aTexture, const char* aPath)
|
|
{
|
|
MLGMappedResource map;
|
|
if (!Map(aTexture, MLGMapType::READ, &map)) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
|
|
map.mData,
|
|
map.mStride,
|
|
aTexture->GetSize(),
|
|
SurfaceFormat::B8G8R8A8);
|
|
gfxUtils::WriteAsPNG(surface, aPath);
|
|
|
|
Unmap(aTexture);
|
|
}
|
|
|
|
RefPtr<MLGTexture>
|
|
MLGDevice::CopyAndCreateReadbackTexture(MLGTexture* aTexture)
|
|
{
|
|
RefPtr<MLGTexture> copy = CreateTexture(
|
|
aTexture->GetSize(),
|
|
SurfaceFormat::B8G8R8A8,
|
|
MLGUsage::Staging,
|
|
MLGTextureFlags::None);
|
|
if (!copy) {
|
|
return nullptr;
|
|
}
|
|
CopyTexture(
|
|
copy,
|
|
IntPoint(0, 0),
|
|
aTexture,
|
|
IntRect(IntPoint(0, 0), aTexture->GetSize()));
|
|
return copy;
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|