Skip to content

Commit

Permalink
Implementation of CGContextFlush. microsoft#2046
Browse files Browse the repository at this point in the history
The implementation is based on D2D:EndDraw vs D2D:Flush.
This is mainly due to the fact that D2D goes through D3D, the flush would flush the D2D batch command and will not flush D3D batch for
WicBitmapRenderTargert. Also there is an issue of a potential shadow copy used by D2D, and flush does not copy over the shadow copy into the original buffer.
EndDraw supports both of the issues addressed above.
  • Loading branch information
msft-Jeyaram committed Mar 19, 2017
1 parent 2646eb8 commit 810e930
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 20 deletions.
53 changes: 34 additions & 19 deletions Frameworks/CoreGraphics/CGContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,20 @@ inline HRESULT GetTextRenderingParams(IDWriteRenderingParams* originalParams, ID
return S_OK;
}

inline HRESULT Flush() {
// We cannot use use ID2D1::Flush here, mainly due to:
// 1. Flush flushes the D2D batch, but does not flush the D3D batch, we do not have a way to flush
// the D3D batch for WicBitmapRenderTarget. (Not: During the EndDraw procedure this occurs.)
// 2. Sometimes a shadow copy of our buffer can be used to do the actual rendering, Flush does not force the copy
// over of the shadow copy into the original buffer (EndDraw does)
// Thus the best way is to implement Flush is through EndDraw
if (_beginEndDrawDepth > 0) {
RETURN_IF_FAILED(PopEndDraw(true));
PushBeginDraw(true);
}
return S_OK;
}

inline void SetShouldAntialias(__CGTrinary shouldAntialias) {
CurrentGState().shouldAntialias = shouldAntialias;
}
Expand Down Expand Up @@ -490,17 +504,17 @@ inline bool ShouldDraw() {
return SUCCEEDED(_firstErrorHr) && CurrentGState().ShouldDraw();
}

inline void PushBeginDraw() {
if ((_beginEndDrawDepth)++ == 0) {
inline void PushBeginDraw(bool forceBeginDraw = false) {
if (forceBeginDraw || ((_beginEndDrawDepth)++ == 0)) {
if (SUCCEEDED(_firstErrorHr)) {
deviceContext->BeginDraw();
}
}
}

inline HRESULT PopEndDraw() {
inline HRESULT PopEndDraw(bool forceEndDraw = false) {
HRESULT hr = S_OK;
if (--(_beginEndDrawDepth) == 0) {
if (forceEndDraw || (--(_beginEndDrawDepth) == 0)) {
hr = deviceContext->EndDraw();
if (_useEnhancedErrorHandling && SUCCEEDED(_firstErrorHr) && FAILED(hr)) {
// If we haven't yet stored an error, and we're about to return an error (not S_OK), store it.
Expand Down Expand Up @@ -612,11 +626,12 @@ void CGContextRestoreGState(CGContextRef context) {

#pragma region Global State - Context Maintenance
/**
@Status Stub
@Status Interoperable
@Notes The flush can operate on a CGBitmapContext.
*/
void CGContextFlush(CGContextRef context) {
NOISY_RETURN_IF_NULL(context);
UNIMPLEMENTED();
FAIL_FAST_IF_FAILED(context->Flush());
}

/**
Expand Down Expand Up @@ -2522,7 +2537,7 @@ HRESULT Stage(CGContextRef context, ID2D1DeviceContext* deviceContext) override
deviceContext->Clear({ 0, 0, 0, 0 }); // Clear the original target to transparent black.
RETURN_IF_FAILED(inputBitmap.As(&copiedImage));
}
return __super::Stage(context, deviceContext);
return __super ::Stage(context, deviceContext);
}
};

Expand Down Expand Up @@ -2570,13 +2585,13 @@ HRESULT Stage(CGContextRef context, ID2D1DeviceContext* deviceContext) override
RETURN_IF_FAILED(deviceContext->CreateEffect(CLSID_D2D1Composite, &_compositeEffect));
_compositeEffect->SetInputEffect(0, affineTransformEffect.Get());

return __super::Stage(context, deviceContext);
return __super ::Stage(context, deviceContext);
}

HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) override {
RETURN_IF_FAILED(__super::Complete(context, deviceContext));
_shadowEffect->SetInput(0, __super::commandList.Get());
_compositeEffect->SetInput(1, __super::commandList.Get());
RETURN_IF_FAILED(__super ::Complete(context, deviceContext));
_shadowEffect->SetInput(0, __super ::commandList.Get());
_compositeEffect->SetInput(1, __super ::commandList.Get());
deviceContext->DrawImage(_compositeEffect.Get());
return S_OK;
}
Expand All @@ -2597,7 +2612,7 @@ HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) overri
// into the member "copiedImage".

HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) override {
RETURN_IF_FAILED(__super::Complete(context, deviceContext));
RETURN_IF_FAILED(__super ::Complete(context, deviceContext));

// The blend effect takes two images and a blend mode.
//
Expand All @@ -2609,8 +2624,8 @@ HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) overri
ComPtr<ID2D1Effect> blendEffect;
RETURN_IF_FAILED(deviceContext->CreateEffect(CLSID_D2D1Blend, &blendEffect));
blendEffect->SetValue(D2D1_BLEND_PROP_MODE, _blendMode);
blendEffect->SetInput(0, __super::copiedImage.Get());
blendEffect->SetInput(1, __super::commandList.Get());
blendEffect->SetInput(0, __super ::copiedImage.Get());
blendEffect->SetInput(1, __super ::commandList.Get());

deviceContext->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
deviceContext->DrawImage(blendEffect.Get());
Expand Down Expand Up @@ -2638,12 +2653,12 @@ HRESULT Stage(CGContextRef context, ID2D1DeviceContext* deviceContext) override
nullptr,
D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND),
nullptr);
return __super::Stage(context, deviceContext);
return __super ::Stage(context, deviceContext);
}

HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) override {
RETURN_IF_FAILED(__super::Complete(context, deviceContext));
deviceContext->DrawImage(__super::commandList.Get(), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, _compositeMode);
RETURN_IF_FAILED(__super ::Complete(context, deviceContext));
deviceContext->DrawImage(__super ::commandList.Get(), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, _compositeMode);
deviceContext->PopLayer();
deviceContext->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
return S_OK;
Expand All @@ -2660,8 +2675,8 @@ HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) overri
}

HRESULT Complete(CGContextRef context, ID2D1DeviceContext* deviceContext) override {
RETURN_IF_FAILED(__super::Complete(context, deviceContext));
deviceContext->DrawImage(__super::commandList.Get(), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, _compositeMode);
RETURN_IF_FAILED(__super ::Complete(context, deviceContext));
deviceContext->DrawImage(__super ::commandList.Get(), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, _compositeMode);
return S_OK;
}
};
Expand Down
2 changes: 1 addition & 1 deletion include/CoreGraphics/CGContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ typedef CF_OPTIONS(CFIndex, CGTextDrawingMode) {
};
// clang-format on

COREGRAPHICS_EXPORT void CGContextFlush(CGContextRef c) STUB_METHOD;
COREGRAPHICS_EXPORT void CGContextFlush(CGContextRef c);
COREGRAPHICS_EXPORT CFTypeID CGContextGetTypeID();
COREGRAPHICS_EXPORT void CGContextRelease(CGContextRef c);
COREGRAPHICS_EXPORT CGContextRef CGContextRetain(CGContextRef c);
Expand Down

0 comments on commit 810e930

Please sign in to comment.