mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Add software mix-blend mode support to the D3D9 compositor. (bug 1243071 part 3, r=bas,mattwoodrow)
This commit is contained in:
parent
81b1d74b67
commit
b4a533e07e
@ -358,7 +358,8 @@ gfx::IntRect
|
||||
Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect,
|
||||
const gfx::Rect& aClipRect,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
gfx::Matrix4x4* aOutTransform)
|
||||
gfx::Matrix4x4* aOutTransform,
|
||||
gfx::Rect* aOutLayerQuad)
|
||||
{
|
||||
// Compute the clip.
|
||||
gfx::IntPoint rtOffset = GetCurrentRenderTarget()->GetOrigin();
|
||||
@ -377,6 +378,10 @@ Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect,
|
||||
// Ensure we don't round out to -1, which trips up Direct3D.
|
||||
dest.IntersectRect(dest, gfx::RectDouble(0, 0, rtSize.width, rtSize.height));
|
||||
|
||||
if (aOutLayerQuad) {
|
||||
*aOutLayerQuad = gfx::Rect(dest.x, dest.y, dest.width, dest.height);
|
||||
}
|
||||
|
||||
// Round out to integer.
|
||||
gfx::IntRect result;
|
||||
dest.RoundOut();
|
||||
|
@ -518,11 +518,16 @@ protected:
|
||||
* Given a layer rect, clip, and transform, compute the area of the backdrop that
|
||||
* needs to be copied for mix-blending. The output transform translates from 0..1
|
||||
* space into the backdrop rect space.
|
||||
*
|
||||
* The transformed layer quad is also optionally returned - this is the same as
|
||||
* the result rect, before rounding.
|
||||
*/
|
||||
gfx::IntRect ComputeBackdropCopyRect(
|
||||
const gfx::Rect& aRect,
|
||||
const gfx::Rect& aClipRect,
|
||||
const gfx::Matrix4x4& aTransform);
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
gfx::Matrix4x4* aOutTransform,
|
||||
gfx::Rect* aOutLayerQuad = nullptr);
|
||||
|
||||
/**
|
||||
* Render time for the current composition.
|
||||
|
@ -73,6 +73,11 @@ CompositorD3D9::GetTextureFactoryIdentifier()
|
||||
ident.mMaxTextureSize = GetMaxTextureSize();
|
||||
ident.mParentBackend = LayersBackend::LAYERS_D3D9;
|
||||
ident.mParentProcessId = XRE_GetProcessType();
|
||||
for (uint8_t op = 0; op < uint8_t(gfx::CompositionOp::OP_COUNT); op++) {
|
||||
if (BlendOpIsMixBlendMode(gfx::CompositionOp(op))) {
|
||||
ident.mSupportedBlendModes += gfx::CompositionOp(op);
|
||||
}
|
||||
}
|
||||
return ident;
|
||||
}
|
||||
|
||||
@ -128,10 +133,10 @@ CompositorD3D9::CreateRenderTarget(const gfx::IntRect &aRect,
|
||||
return MakeAndAddRef<CompositingRenderTargetD3D9>(texture, aInit, aRect);
|
||||
}
|
||||
|
||||
already_AddRefed<CompositingRenderTarget>
|
||||
CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
|
||||
const CompositingRenderTarget *aSource,
|
||||
const gfx::IntPoint &aSourcePoint)
|
||||
already_AddRefed<IDirect3DTexture9>
|
||||
CompositorD3D9::CreateTexture(const gfx::IntRect& aRect,
|
||||
const CompositingRenderTarget* aSource,
|
||||
const gfx::IntPoint& aSourcePoint)
|
||||
{
|
||||
MOZ_ASSERT(aRect.width != 0 && aRect.height != 0, "Trying to create a render target of invalid size");
|
||||
|
||||
@ -189,6 +194,16 @@ CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
|
||||
}
|
||||
}
|
||||
|
||||
return texture.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CompositingRenderTarget>
|
||||
CompositorD3D9::CreateRenderTargetFromSource(const gfx::IntRect &aRect,
|
||||
const CompositingRenderTarget *aSource,
|
||||
const gfx::IntPoint &aSourcePoint)
|
||||
{
|
||||
RefPtr<IDirect3DTexture9> texture = CreateTexture(aRect, aSource, aSourcePoint);
|
||||
|
||||
return MakeAndAddRef<CompositingRenderTargetD3D9>(texture,
|
||||
INIT_MODE_NONE,
|
||||
aRect);
|
||||
@ -288,6 +303,40 @@ CompositorD3D9::DrawQuad(const gfx::Rect &aRect,
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Rect backdropDest;
|
||||
gfx::IntRect backdropRect;
|
||||
gfx::Matrix4x4 backdropTransform;
|
||||
RefPtr<IDirect3DTexture9> backdropTexture;
|
||||
gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
|
||||
|
||||
if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
|
||||
EffectBlendMode *blendEffect =
|
||||
static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
|
||||
blendMode = blendEffect->mBlendMode;
|
||||
|
||||
// Pixel Shader Model 2.0 is too limited to perform blending in the same way
|
||||
// as Direct3D 11 - there are too many instructions, and we don't have
|
||||
// configurable shaders (as we do with OGL) that would avoid a huge shader
|
||||
// matrix.
|
||||
//
|
||||
// Instead, we use a multi-step process for blending on D3D9:
|
||||
// (1) Capture the backdrop into a temporary surface.
|
||||
// (2) Render the effect chain onto the backdrop, with OP_SOURCE.
|
||||
// (3) Capture the backdrop again into another surface - these are our source pixels.
|
||||
// (4) Perform a final blend step using software.
|
||||
// (5) Blit the blended result back to the render target.
|
||||
if (BlendOpIsMixBlendMode(blendMode)) {
|
||||
backdropRect = ComputeBackdropCopyRect(
|
||||
aRect, aClipRect, aTransform, &backdropTransform, &backdropDest);
|
||||
|
||||
// If this fails, don't set a blend op.
|
||||
backdropTexture = CreateTexture(backdropRect, mCurrentRT, backdropRect.TopLeft());
|
||||
if (!backdropTexture) {
|
||||
blendMode = gfx::CompositionOp::OP_OVER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RECT scissor;
|
||||
scissor.left = aClipRect.x;
|
||||
scissor.right = aClipRect.XMost();
|
||||
@ -474,15 +523,35 @@ CompositorD3D9::DrawQuad(const gfx::Rect &aRect,
|
||||
|
||||
SetMask(aEffectChain, maskTexture);
|
||||
|
||||
if (BlendOpIsMixBlendMode(blendMode)) {
|
||||
// Use SOURCE instead of OVER to get the original source pixels without
|
||||
// having to render to another intermediate target.
|
||||
d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO);
|
||||
}
|
||||
if (!isPremultiplied) {
|
||||
d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
}
|
||||
|
||||
d3d9Device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
|
||||
|
||||
// Restore defaults.
|
||||
if (BlendOpIsMixBlendMode(blendMode)) {
|
||||
d3d9Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
}
|
||||
if (!isPremultiplied) {
|
||||
d3d9Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
|
||||
}
|
||||
|
||||
// Final pass - if mix-blending, do it now that we have the backdrop and
|
||||
// source textures.
|
||||
if (BlendOpIsMixBlendMode(blendMode)) {
|
||||
FinishMixBlend(
|
||||
backdropRect,
|
||||
backdropDest,
|
||||
backdropTransform,
|
||||
backdropTexture,
|
||||
blendMode);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -777,5 +846,187 @@ CompositorD3D9::ReportFailure(const nsACString &aMsg, HRESULT aCode)
|
||||
gfx::LogFailure(msg);
|
||||
}
|
||||
|
||||
static inline already_AddRefed<IDirect3DSurface9>
|
||||
GetSurfaceOfTexture(IDirect3DTexture9* aTexture)
|
||||
{
|
||||
RefPtr<IDirect3DSurface9> surface;
|
||||
HRESULT hr = aTexture->GetSurfaceLevel(0, getter_AddRefs(surface));
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalNote << "Failed to grab texture surface " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
static inline already_AddRefed<IDirect3DSurface9>
|
||||
CreateDataSurfaceForTexture(IDirect3DDevice9* aDevice,
|
||||
IDirect3DSurface9* aSource,
|
||||
const D3DSURFACE_DESC& aDesc)
|
||||
{
|
||||
RefPtr<IDirect3DSurface9> dest;
|
||||
HRESULT hr = aDevice->CreateOffscreenPlainSurface(
|
||||
aDesc.Width, aDesc.Height,
|
||||
aDesc.Format, D3DPOOL_SYSTEMMEM,
|
||||
getter_AddRefs(dest), nullptr);
|
||||
if (FAILED(hr) || !dest) {
|
||||
gfxCriticalNote << "Failed to create offscreen plain surface " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
hr = aDevice->GetRenderTargetData(aSource, dest);
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalNote << "Failed to get render target data " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dest.forget();
|
||||
}
|
||||
|
||||
class AutoSurfaceLock
|
||||
{
|
||||
public:
|
||||
AutoSurfaceLock(IDirect3DSurface9* aSurface, DWORD aFlags = 0) {
|
||||
PodZero(&mRect);
|
||||
|
||||
HRESULT hr = aSurface->LockRect(&mRect, nullptr, aFlags);
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalNote << "Failed to lock surface rect " << hexa(hr);
|
||||
return;
|
||||
}
|
||||
mSurface = aSurface;
|
||||
}
|
||||
~AutoSurfaceLock() {
|
||||
if (mSurface) {
|
||||
mSurface->UnlockRect();
|
||||
}
|
||||
}
|
||||
|
||||
bool Okay() const {
|
||||
return !!mSurface;
|
||||
}
|
||||
int Pitch() const {
|
||||
MOZ_ASSERT(Okay());
|
||||
return mRect.Pitch;
|
||||
}
|
||||
uint8_t* Bits() const {
|
||||
MOZ_ASSERT(Okay());
|
||||
return reinterpret_cast<uint8_t*>(mRect.pBits);
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<IDirect3DSurface9> mSurface;
|
||||
D3DLOCKED_RECT mRect;
|
||||
};
|
||||
|
||||
void
|
||||
CompositorD3D9::FinishMixBlend(const gfx::IntRect& aBackdropRect,
|
||||
const gfx::Rect& aBackdropDest,
|
||||
const gfx::Matrix4x4& aBackdropTransform,
|
||||
RefPtr<IDirect3DTexture9> aBackdrop,
|
||||
gfx::CompositionOp aBlendMode)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
RefPtr<IDirect3DTexture9> source =
|
||||
CreateTexture(aBackdropRect, mCurrentRT, aBackdropRect.TopLeft());
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Slow path - do everything in software. Unfortunately this requires
|
||||
// a lot of copying, since we have to readback the source and backdrop,
|
||||
// then upload the blended result, then blit it back.
|
||||
|
||||
IDirect3DDevice9* d3d9Device = device();
|
||||
|
||||
// Query geometry/format of the two surfaces.
|
||||
D3DSURFACE_DESC backdropDesc, sourceDesc;
|
||||
if (FAILED(aBackdrop->GetLevelDesc(0, &backdropDesc)) ||
|
||||
FAILED(source->GetLevelDesc(0, &sourceDesc)))
|
||||
{
|
||||
gfxCriticalNote << "Failed to query mix-blend texture descriptor";
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(backdropDesc.Format == D3DFMT_A8R8G8B8);
|
||||
MOZ_ASSERT(sourceDesc.Format == D3DFMT_A8R8G8B8);
|
||||
|
||||
// Acquire a temporary data surface for the backdrop texture.
|
||||
RefPtr<IDirect3DSurface9> backdropSurface = GetSurfaceOfTexture(aBackdrop);
|
||||
if (!backdropSurface) {
|
||||
return;
|
||||
}
|
||||
RefPtr<IDirect3DSurface9> tmpBackdrop =
|
||||
CreateDataSurfaceForTexture(d3d9Device, backdropSurface, backdropDesc);
|
||||
if (!tmpBackdrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
// New scope for locks and temporary surfaces.
|
||||
{
|
||||
// Acquire a temporary data surface for the source texture.
|
||||
RefPtr<IDirect3DSurface9> sourceSurface = GetSurfaceOfTexture(source);
|
||||
if (!sourceSurface) {
|
||||
return;
|
||||
}
|
||||
RefPtr<IDirect3DSurface9> tmpSource =
|
||||
CreateDataSurfaceForTexture(d3d9Device, sourceSurface, sourceDesc);
|
||||
if (!tmpSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the readback and blend in software.
|
||||
AutoSurfaceLock backdropLock(tmpBackdrop);
|
||||
AutoSurfaceLock sourceLock(tmpSource, D3DLOCK_READONLY);
|
||||
if (!backdropLock.Okay() || !sourceLock.Okay()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<DataSourceSurface> source = Factory::CreateWrappingDataSourceSurface(
|
||||
sourceLock.Bits(), sourceLock.Pitch(),
|
||||
gfx::IntSize(sourceDesc.Width, sourceDesc.Height),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
|
||||
RefPtr<DrawTarget> dest = Factory::CreateDrawTargetForData(
|
||||
BackendType::CAIRO,
|
||||
backdropLock.Bits(),
|
||||
gfx::IntSize(backdropDesc.Width, backdropDesc.Height),
|
||||
backdropLock.Pitch(),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
|
||||
// The backdrop rect is rounded out - account for any difference between
|
||||
// it and the actual destination.
|
||||
gfx::Rect destRect(
|
||||
aBackdropDest.x - aBackdropRect.x,
|
||||
aBackdropDest.y - aBackdropRect.y,
|
||||
aBackdropDest.width,
|
||||
aBackdropDest.height);
|
||||
|
||||
dest->DrawSurface(
|
||||
source, destRect, destRect,
|
||||
gfx::DrawSurfaceOptions(),
|
||||
gfx::DrawOptions(1.0f, aBlendMode));
|
||||
}
|
||||
|
||||
// Upload the new blended surface to the backdrop texture.
|
||||
d3d9Device->UpdateSurface(tmpBackdrop, nullptr, backdropSurface, nullptr);
|
||||
|
||||
// Finally, drop in the new backdrop. We don't need to do another
|
||||
// DrawPrimitive() since the software blend will have included the
|
||||
// final OP_OVER step for us.
|
||||
RECT destRect = {
|
||||
aBackdropRect.x, aBackdropRect.y,
|
||||
aBackdropRect.XMost(), aBackdropRect.YMost()
|
||||
};
|
||||
hr = d3d9Device->StretchRect(backdropSurface,
|
||||
nullptr,
|
||||
mCurrentRT->GetD3D9Surface(),
|
||||
&destRect,
|
||||
D3DTEXF_NONE);
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalNote << "StretcRect with mix-blend failed " << hexa(hr);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -137,6 +137,20 @@ private:
|
||||
*/
|
||||
bool EnsureSwapChain();
|
||||
|
||||
already_AddRefed<IDirect3DTexture9>
|
||||
CreateTexture(const gfx::IntRect& aRect,
|
||||
const CompositingRenderTarget* aSource,
|
||||
const gfx::IntPoint& aSourcePoint);
|
||||
|
||||
/**
|
||||
* Complete a mix-blend step at the end of DrawQuad().
|
||||
*/
|
||||
void FinishMixBlend(const gfx::IntRect& aBackdropRect,
|
||||
const gfx::Rect& aBackdropDest,
|
||||
const gfx::Matrix4x4& aBackdropTransform,
|
||||
RefPtr<IDirect3DTexture9> aBackdrop,
|
||||
gfx::CompositionOp aBlendMode);
|
||||
|
||||
/**
|
||||
* DeviceManagerD3D9 keeps a count of the number of times its device is
|
||||
* reset or recreated. We keep a parallel count (mDeviceResetCount). It
|
||||
|
@ -1124,16 +1124,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
|
||||
}
|
||||
|
||||
if (BlendOpIsMixBlendMode(blendMode)) {
|
||||
gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform);
|
||||
mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
|
||||
|
||||
// Create a transform from adjusted clip space to render target space,
|
||||
// translate it for the backdrop rect, then transform it into the backdrop's
|
||||
// uv-space.
|
||||
gfx::Matrix4x4 transform;
|
||||
transform.PostScale(mRenderBounds.width, mRenderBounds.height, 1.0);
|
||||
transform.PostTranslate(-rect.x, -rect.y, 0.0);
|
||||
transform.PostScale(1 / float(rect.width), 1 / float(rect.height), 1.0);
|
||||
gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform, &transform);
|
||||
mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
|
||||
program->SetBackdropTransform(transform);
|
||||
}
|
||||
|
||||
|
@ -656,6 +656,8 @@ function BuildConditionSandbox(aURL) {
|
||||
gWindowUtils.layerManagerType != "Basic";
|
||||
sandbox.d3d11 =
|
||||
gWindowUtils.layerManagerType == "Direct3D 11";
|
||||
sandbox.d3d9 =
|
||||
gWindowUtils.layerManagerType == "Direct3D 9";
|
||||
sandbox.layersOpenGL =
|
||||
gWindowUtils.layerManagerType == "OpenGL";
|
||||
sandbox.layersOMTC =
|
||||
|
Loading…
Reference in New Issue
Block a user