/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/StaticPrefs_layers.h" #include "gfxUtils.h" #include "ShaderDefinitionsMLGPU.h" #include "SharedBufferMLGPU.h" #include "UtilityMLGPU.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& aExtraRect) { // We clamp the invalid region to the backbuffer size, otherwise the present // can fail. IntRect bounds(IntPoint(0, 0), GetSize()); nsIntRegion invalid = std::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), mInitialized(false), mIsValid(false), mCanUseClearView(false), mCanUseConstantBufferOffsetBinding(false), mMaxConstantBufferBindSize(0) {} MLGDevice::~MLGDevice() = default; 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 (!StaticPrefs::layers_mlgpu_enable_buffer_sharing_AtStartup()) { 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 (!StaticPrefs::layers_mlgpu_enable_clear_view_AtStartup()) { 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(this, kDefaultVertexBufferSize); mSharedVSBuffer = MakeUnique(this, kDefaultVSConstantBufferSize); mSharedPSBuffer = MakeUnique(this, kDefaultPSConstantBufferSize); if (!mSharedVertexBuffer->Init() || !mSharedVSBuffer->Init() || !mSharedPSBuffer->Init()) { return Fail("FEATURE_FAILURE_ALLOC_SHARED_BUFFER", "Failed to allocate a shared shader buffer"); } if (StaticPrefs::layers_mlgpu_enable_buffer_cache_AtStartup()) { mConstantBufferCache = MakeUnique(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 MLGDevice::GetBufferForColorSpace(YUVColorSpace aColorSpace) { if (mColorSpaceBuffers[aColorSpace]) { return mColorSpaceBuffers[aColorSpace]; } YCbCrShaderConstants buffer; memcpy(&buffer.yuvColorMatrix, gfxUtils::YuvToRgbMatrix4x3RowMajor(aColorSpace), sizeof(buffer.yuvColorMatrix)); RefPtr resource = CreateBuffer( MLGBufferType::Constant, sizeof(buffer), MLGUsage::Immutable, &buffer); if (!resource) { return nullptr; } mColorSpaceBuffers[aColorSpace] = resource; return resource; } RefPtr MLGDevice::GetBufferForColorDepthCoefficient( ColorDepth aColorDepth) { if (mColorDepthBuffers[aColorDepth]) { return mColorDepthBuffers[aColorDepth]; } YCbCrColorDepthConstants buffer; buffer.coefficient = gfx::RescalingFactorForColorDepth(aColorDepth); RefPtr resource = CreateBuffer( MLGBufferType::Constant, sizeof(buffer), MLGUsage::Immutable, &buffer); if (!resource) { return nullptr; } mColorDepthBuffers[aColorDepth] = resource; return resource; } bool MLGDevice::Synchronize() { return true; } void MLGDevice::PrepareClearRegion(ClearRegionHelper* aOut, nsTArray&& aRects, const Maybe& aSortIndex) { if (CanUseClearView() && !aSortIndex) { aOut->mRects = std::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()) { DeviceColor 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 surface = Factory::CreateWrappingDataSourceSurface( map.mData, map.mStride, aTexture->GetSize(), SurfaceFormat::B8G8R8A8); gfxUtils::WriteAsPNG(surface, aPath); Unmap(aTexture); } RefPtr MLGDevice::CopyAndCreateReadbackTexture( MLGTexture* aTexture) { RefPtr 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