diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 95dc6d3abaa7..56d585995567 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -83,13 +83,9 @@ toolbar[printpreview="true"] { } %ifdef MENUBAR_CAN_AUTOHIDE -#main-window[inFullscreen] > #titlebar { +#main-window[inFullscreen] > #appmenu-button-container { display: none; } - -#titlebar { - -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox"); -} %endif toolbarpaletteitem[place="palette"] > toolbaritem > hbox[type="places"] { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 5fca5d0e1770..27b471d3852e 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1320,15 +1320,6 @@ function prepareForStartup() { // setup simple gestures support gGestureSupport.init(true); - -#ifdef MENUBAR_CAN_AUTOHIDE - // update the visibility of the titlebar buttons after the window is - // displayed. (required by theme code.) - window.addEventListener("MozAfterPaint", function () { - window.removeEventListener("MozAfterPaint", arguments.callee, false); - document.getElementById("titlebar-buttonbox").collapsed = false; - }, false); -#endif } function delayedStartup(isLoadingBlank, mustLoadSidebar) { @@ -4769,20 +4760,13 @@ function updateAppButtonDisplay() { window.menubar.visible && document.getElementById("toolbar-menubar").getAttribute("autohide") == "true"; - document.getElementById("titlebar").hidden = !displayAppButton; + document.getElementById("appmenu-button-container").hidden = !displayAppButton; if (displayAppButton) document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1"); else document.documentElement.removeAttribute("chromemargin"); } - -function onTitlebarMaxClick() { - if (window.windowState == window.STATE_MAXIMIZED) - window.restore(); - else - window.maximize(); -} #endif function displaySecurityInfo() diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 645b260d1bd6..c08cbed4cf51 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -437,9 +437,7 @@ #ifdef MENUBAR_CAN_AUTOHIDE - - - + - - - - - - - - - + #endif #titlebar { - -moz-appearance: -moz-window-titlebar-maximized; -} - -#titlebar-content { - margin-left: 15px; - margin-right: 15px; -} - -/* aesthetic - push the fx button off the top window border */ -@media not all and (-moz-windows-classic) { - #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #appmenu-button-container { -%ifndef WINSTRIPE_AERO - margin-top: 1px; -%else - margin-top: 2px; +/* XXX: stop-gap until the button can be drawn in the title bar */ +%ifdef WINSTRIPE_AERO +@media not all and (-moz-windows-compositor) { %endif + #main-window[tabsontop="true"] > #appmenu-button-container > #appmenu-button { + position: relative !important; + margin-bottom: -1.6em !important; } -} - -#titlebar-buttonbox { - -moz-appearance: -moz-window-button-box; - -moz-box-align: start; -} - -#main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox { - -moz-appearance: -moz-window-button-box-maximized; -} - -/* titlebar command buttons */ - -#titlebar-min { - -moz-appearance: -moz-window-button-minimize; -} - -#titlebar-max { - -moz-appearance: -moz-window-button-maximize; -} - -#main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox > #titlebar-max { - -moz-appearance: -moz-window-button-restore; -} - -#titlebar-close { - -moz-appearance: -moz-window-button-close; -} - -@media not all and (-moz-windows-classic) { - #titlebar-min { - -moz-margin-end: 1px; + #navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] { + position: relative !important; + background-color: -moz-dialog !important; } - - #titlebar-max { - -moz-margin-start: 1px; - -moz-margin-end: 1px; - } - - #titlebar-close { - -moz-margin-start: 1px; - -moz-margin-end: 0; - } -} - -@media all and (-moz-windows-classic) { - #titlebar-close { - -moz-margin-start: 2px !important; + #navigator-toolbox[tabsontop="true"] > #toolbar-menubar[autohide="true"] ~ #TabsToolbar:not([inFullscreen]) { + -moz-padding-start: 10em !important; } +%ifdef WINSTRIPE_AERO } +%endif /* ::::: bookmark buttons ::::: */ diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 823ca70b9cd7..918d33ec4f2e 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1100,6 +1100,10 @@ nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag) { nsresult rv = NS_OK; + if (mIgnoreNextCloseHead && aTag == eHTMLTag_head) { + mIgnoreNextCloseHead = PR_FALSE; + return NS_OK; + } if (mSkip) { mSkip = PR_FALSE; return rv; @@ -1229,7 +1233,10 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; - if (mSkip) { + // We need to explicitly skip adding leaf nodes in the paranoid sink, + // otherwise things like the textnode under get appended to + // the fragment itself, and won't be popped off in CloseContainer. + if (mSkip || mIgnoreNextCloseHead) { return rv; } diff --git a/dom/plugins/PluginModuleParent.cpp b/dom/plugins/PluginModuleParent.cpp index b136a4aefa8d..6859f7119ffa 100644 --- a/dom/plugins/PluginModuleParent.cpp +++ b/dom/plugins/PluginModuleParent.cpp @@ -54,6 +54,7 @@ #include "mozilla/plugins/BrowserStreamParent.h" #include "PluginIdentifierParent.h" +#include "nsAutoPtr.h" #include "nsContentUtils.h" #include "nsCRT.h" #ifdef MOZ_CRASHREPORTER @@ -69,6 +70,7 @@ using mozilla::ipc::SyncChannel; using namespace mozilla::plugins; static const char kTimeoutPref[] = "dom.ipc.plugins.timeoutSecs"; +static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs"; template<> struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent> @@ -84,15 +86,21 @@ PluginModuleParent::LoadModule(const char* aFilePath) { PLUGIN_LOG_DEBUG_FUNCTION; + PRInt32 prefSecs = nsContentUtils::GetIntPref(kLaunchTimeoutPref, 0); + // Block on the child process being launched and initialized. - PluginModuleParent* parent = new PluginModuleParent(aFilePath); - parent->mSubprocess->Launch(); + nsAutoPtr<PluginModuleParent> parent(new PluginModuleParent(aFilePath)); + bool launched = parent->mSubprocess->Launch(prefSecs * 1000); + if (!launched) { + // Need to set this so the destructor doesn't complain. + parent->mShutdown = true; + return nsnull; + } parent->Open(parent->mSubprocess->GetChannel(), parent->mSubprocess->GetChildProcessHandle()); TimeoutChanged(kTimeoutPref, parent); - - return parent; + return parent.forget(); } diff --git a/dom/plugins/PluginProcessParent.cpp b/dom/plugins/PluginProcessParent.cpp index 7ff4bd98f2d3..984fae82a502 100644 --- a/dom/plugins/PluginProcessParent.cpp +++ b/dom/plugins/PluginProcessParent.cpp @@ -68,11 +68,11 @@ PluginProcessParent::~PluginProcessParent() } bool -PluginProcessParent::Launch() +PluginProcessParent::Launch(PRInt32 timeoutMs) { vector<string> args; args.push_back(MungePluginDsoPath(mPluginFilePath)); - return SyncLaunch(args); + return SyncLaunch(args, timeoutMs); } void diff --git a/dom/plugins/PluginProcessParent.h b/dom/plugins/PluginProcessParent.h index fad22a4adb83..2755c2642fa3 100644 --- a/dom/plugins/PluginProcessParent.h +++ b/dom/plugins/PluginProcessParent.h @@ -61,9 +61,10 @@ public: ~PluginProcessParent(); /** - * Synchronously launch the plugin process. + * Synchronously launch the plugin process. If the process fails to launch + * after timeoutMs, this method will return false. */ - bool Launch(); + bool Launch(PRInt32 timeoutMs); void Delete(); diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index 8bf41534ba09..3b244b5f45e1 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -52,6 +52,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=520182 <div id="jj" contenteditable="true"></div> <iframe id="kk" src="about:blank"></iframe> <div id="ll" contenteditable="true"></div> + <iframe id="mm" src="about:blank"></iframe> + <div id="nn" contenteditable="true"></div> </div> <pre id="test"> <script type="application/javascript"> @@ -73,6 +75,7 @@ const invalidStyle3Payload = "foo<style>@import 'xxx.css';</style>baz"; const invalidStyle4Payload = "foo<span style=\"@import 'xxx.css';\">bar</span>baz"; const invalidStyle5Payload = "foo<span style=\"@font-face{font-family:xxx;src:'xxx.ttf';}\">bar</span>baz"; const invalidStyle6Payload = "foo<span style=\"@namespace xxx url(http://example.com/);\">bar</span>baz"; +const invalidStyle7Payload = "<html><head><title>xxxfoo"; const nestedStylePayload = "foo#bar2{-moz-binding:url('data:text/xml,');baz"; const validImgSrc1Payload = "foobaz"; const validImgSrc2Payload = "foobaz"; @@ -199,26 +202,26 @@ var tests = [ { id: "s", isIFrame: true, - payload: invalidStyle1Payload, + payload: invalidStyle3Payload, rootElement: function() document.getElementById("s").contentDocument.documentElement, checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "t", - payload: invalidStyle1Payload, + payload: invalidStyle3Payload, rootElement: function() document.getElementById("t"), checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "u", isIFrame: true, - payload: invalidStyle2Payload, + payload: invalidStyle4Payload, rootElement: function() document.getElementById("u").contentDocument.documentElement, checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "v", - payload: invalidStyle2Payload, + payload: invalidStyle4Payload, rootElement: function() document.getElementById("v"), checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, @@ -298,41 +301,62 @@ var tests = [ { id: "gg", isIFrame: true, - payload: invalidStyle6Payload, + payload: validImgSrc1Payload, rootElement: function() document.getElementById("gg").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "hh", - payload: invalidStyle6Payload, + payload: validImgSrc1Payload, rootElement: function() document.getElementById("hh"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "ii", isIFrame: true, - payload: invalidStyle6Payload, + payload: validImgSrc2Payload, rootElement: function() document.getElementById("ii").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "jj", - payload: invalidStyle6Payload, + payload: validImgSrc2Payload, rootElement: function() document.getElementById("jj"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "kk", isIFrame: true, - payload: invalidStyle6Payload, + payload: validImgSrc3Payload, rootElement: function() document.getElementById("kk").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "ll", - payload: invalidStyle6Payload, + payload: validImgSrc3Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") + }, + { + id: "mm", + isIFrame: true, + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("mm").contentDocument.documentElement, + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } + }, + { + id: "nn", + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("nn"), + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } } ]; @@ -357,28 +381,37 @@ function runTest(test) { } else elem.focus(); - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if ("insertHTML" in test) { + if ("isIFrame" in test) { + elem.contentDocument.execCommand("inserthtml", false, test.payload); + } else { + getSelection().collapse(elem, 0); + document.execCommand("inserthtml", false, test.payload); + } + } else { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); + var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] + .getService(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - var data = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - data.data = test.payload; - trans.addDataFlavor("text/html"); - trans.setTransferData("text/html", data, data.data.length * 2); - clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + var data = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + data.data = test.payload; + trans.addDataFlavor("text/html"); + trans.setTransferData("text/html", data, data.data.length * 2); + clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); - var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindow); + var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); - mainWindow.goDoCommand("cmd_paste"); + mainWindow.goDoCommand("cmd_paste"); + } if ("checkResult" in test) { if ("isIFrame" in test) { diff --git a/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp b/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp index 3316a7b73c9f..4f86a915b542 100644 --- a/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp +++ b/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp @@ -107,7 +107,7 @@ static JSContext *autoconfig_cx = nsnull; static JSObject *autoconfig_glob; static JSClass global_class = { - "autoconfig_global", 0, + "autoconfig_global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nsnull }; diff --git a/gfx/cairo/cairo/src/cairo-d2d-private-fx.h b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h new file mode 100644 index 000000000000..15945339ff3a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h @@ -0,0 +1,546 @@ +#if 0 +// +// FX Version: fx_4_0 +// Child effect (requires effect pool): false +// +// 1 local buffer(s) +// +cbuffer cb0 +{ + float4 QuadDesc; // Offset: 0, size: 16 + float4 TexCoords; // Offset: 16, size: 16 +} + +// +// 2 local object(s) +// +Texture2D tex; +SamplerState sSampler +{ + Texture = tex; + AddressU = uint(CLAMP /* 3 */); + AddressV = uint(CLAMP /* 3 */); +}; + +// +// 1 technique(s) +// +technique10 SampleTexture +{ + pass P0 + { + VertexShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 + // + // + // Buffer Definitions: + // + // cbuffer cb0 + // { + // + // float4 QuadDesc; // Offset: 0 Size: 16 + // float4 TexCoords; // Offset: 16 Size: 16 + // + // } + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cb0 cbuffer NA NA 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // POSITION 0 xyz 0 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float xyzw + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Constant buffer to DX9 shader constant mappings: + // + // Target Reg Buffer Start Reg # of Regs Data Conversion + // ---------- ------- --------- --------- ---------------------- + // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) + // + // + // Runtime generated constant mappings: + // + // Target Reg Constant Description + // ---------- -------------------------------------------------- + // c0 Vertex Shader position offset + // + // + // Level9 shader bytecode: + // + vs_2_x + def c3, 0, 1, 0, 0 + dcl_texcoord v0 + mad oT0.xy, v0, c2.zwzw, c2 + mad r0.x, v0.x, c1.z, c1.x + mad r0.y, v0.y, c1.w, c1.y + add oPos.xy, r0, c0 + mov oPos.zw, c3.xyxy + + // approximately 5 instruction slots used + vs_4_0 + dcl_constantbuffer cb0[2], immediateIndexed + dcl_input v0.xy + dcl_output_siv o0.xyzw, position + dcl_output o1.xy + mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx + mov o0.zw, l(0,0,0,1.000000) + mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx + ret + // Approximately 4 instruction slots used + + }; + GeometryShader = NULL; + PixelShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // sSampler sampler NA NA 0 1 + // tex texture float4 2d 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Target 0 xyzw 0 TARGET float xyzw + // + // + // Sampler/Resource to DX9 shader sampler mappings: + // + // Target Sampler Source Sampler Source Resource + // -------------- --------------- ---------------- + // s0 s0 t0 + // + // + // Level9 shader bytecode: + // + ps_2_x + dcl t0.xy + dcl_2d s0 + texld r0, t0, s0 + mov oC0, r0 + + // approximately 2 instruction slots used (1 texture, 1 arithmetic) + ps_4_0 + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + sample o0.xyzw, v1.xyxx, t0.xyzw, s0 + ret + // Approximately 2 instruction slots used + + }; + } + +} + +#endif + +const BYTE g_main[] = +{ + 68, 88, 66, 67, 235, 24, + 238, 6, 37, 230, 191, 228, + 58, 61, 41, 219, 70, 130, + 61, 51, 1, 0, 0, 0, + 187, 8, 0, 0, 1, 0, + 0, 0, 36, 0, 0, 0, + 70, 88, 49, 48, 143, 8, + 0, 0, 1, 16, 255, 254, + 1, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 79, 7, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 99, 98, + 48, 0, 102, 108, 111, 97, + 116, 52, 0, 8, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 16, + 0, 0, 0, 10, 33, 0, + 0, 81, 117, 97, 100, 68, + 101, 115, 99, 0, 84, 101, + 120, 67, 111, 111, 114, 100, + 115, 0, 84, 101, 120, 116, + 117, 114, 101, 50, 68, 0, + 62, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 116, 101, + 120, 0, 83, 97, 109, 112, + 108, 101, 114, 83, 116, 97, + 116, 101, 0, 104, 0, 0, + 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 0, 0, + 0, 115, 83, 97, 109, 112, + 108, 101, 114, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 83, 97, + 109, 112, 108, 101, 84, 101, + 120, 116, 117, 114, 101, 0, + 80, 48, 0, 172, 3, 0, + 0, 68, 88, 66, 67, 247, + 105, 31, 113, 120, 95, 58, + 12, 207, 141, 45, 76, 175, + 59, 223, 25, 1, 0, 0, + 0, 172, 3, 0, 0, 6, + 0, 0, 0, 56, 0, 0, + 0, 248, 0, 0, 0, 188, + 1, 0, 0, 56, 2, 0, + 0, 32, 3, 0, 0, 84, + 3, 0, 0, 65, 111, 110, + 57, 184, 0, 0, 0, 184, + 0, 0, 0, 0, 2, 254, + 255, 132, 0, 0, 0, 52, + 0, 0, 0, 1, 0, 36, + 0, 0, 0, 48, 0, 0, + 0, 48, 0, 0, 0, 36, + 0, 1, 0, 48, 0, 0, + 0, 0, 0, 2, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 254, + 255, 81, 0, 0, 5, 3, + 0, 15, 160, 0, 0, 0, + 0, 0, 0, 128, 63, 0, + 0, 0, 0, 0, 0, 0, + 0, 31, 0, 0, 2, 5, + 0, 0, 128, 0, 0, 15, + 144, 4, 0, 0, 4, 0, + 0, 3, 224, 0, 0, 228, + 144, 2, 0, 238, 160, 2, + 0, 228, 160, 4, 0, 0, + 4, 0, 0, 1, 128, 0, + 0, 0, 144, 1, 0, 170, + 160, 1, 0, 0, 160, 4, + 0, 0, 4, 0, 0, 2, + 128, 0, 0, 85, 144, 1, + 0, 255, 160, 1, 0, 85, + 160, 2, 0, 0, 3, 0, + 0, 3, 192, 0, 0, 228, + 128, 0, 0, 228, 160, 1, + 0, 0, 2, 0, 0, 12, + 192, 3, 0, 68, 160, 255, + 255, 0, 0, 83, 72, 68, + 82, 188, 0, 0, 0, 64, + 0, 1, 0, 47, 0, 0, + 0, 89, 0, 0, 4, 70, + 142, 32, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 95, + 0, 0, 3, 50, 16, 16, + 0, 0, 0, 0, 0, 103, + 0, 0, 4, 242, 32, 16, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 50, 32, 16, 0, 1, + 0, 0, 0, 50, 0, 0, + 11, 50, 32, 16, 0, 0, + 0, 0, 0, 70, 16, 16, + 0, 0, 0, 0, 0, 230, + 138, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 70, + 128, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 54, + 0, 0, 8, 194, 32, 16, + 0, 0, 0, 0, 0, 2, + 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, + 63, 50, 0, 0, 11, 50, + 32, 16, 0, 1, 0, 0, + 0, 70, 16, 16, 0, 0, + 0, 0, 0, 230, 138, 32, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 70, 128, 32, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 62, 0, 0, + 1, 83, 84, 65, 84, 116, + 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, + 68, 69, 70, 224, 0, 0, + 0, 1, 0, 0, 0, 64, + 0, 0, 0, 1, 0, 0, + 0, 28, 0, 0, 0, 0, + 4, 254, 255, 0, 129, 0, + 0, 174, 0, 0, 0, 60, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 99, 98, 48, 0, 60, + 0, 0, 0, 2, 0, 0, + 0, 88, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 136, + 0, 0, 0, 0, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 148, 0, 0, + 0, 0, 0, 0, 0, 164, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 148, 0, 0, + 0, 0, 0, 0, 0, 81, + 117, 97, 100, 68, 101, 115, + 99, 0, 171, 171, 171, 1, + 0, 3, 0, 1, 0, 4, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 101, 120, + 67, 111, 111, 114, 100, 115, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 57, 46, 50, 55, 46, 57, + 53, 50, 46, 51, 48, 50, + 50, 0, 171, 73, 83, 71, + 78, 44, 0, 0, 0, 1, + 0, 0, 0, 8, 0, 0, + 0, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 7, 3, 0, + 0, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 171, 171, + 171, 79, 83, 71, 78, 80, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 3, 12, 0, 0, 83, + 86, 95, 80, 111, 115, 105, + 116, 105, 111, 110, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 171, 171, 171, 195, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, + 0, 188, 2, 0, 0, 68, + 88, 66, 67, 90, 17, 243, + 62, 104, 14, 0, 40, 49, + 70, 150, 92, 77, 1, 115, + 141, 1, 0, 0, 0, 188, + 2, 0, 0, 6, 0, 0, + 0, 56, 0, 0, 0, 164, + 0, 0, 0, 16, 1, 0, + 0, 140, 1, 0, 0, 48, + 2, 0, 0, 136, 2, 0, + 0, 65, 111, 110, 57, 100, + 0, 0, 0, 100, 0, 0, + 0, 0, 2, 255, 255, 60, + 0, 0, 0, 40, 0, 0, + 0, 0, 0, 40, 0, 0, + 0, 40, 0, 0, 0, 40, + 0, 1, 0, 36, 0, 0, + 0, 40, 0, 0, 0, 0, + 0, 1, 2, 255, 255, 31, + 0, 0, 2, 0, 0, 0, + 128, 0, 0, 3, 176, 31, + 0, 0, 2, 0, 0, 0, + 144, 0, 8, 15, 160, 66, + 0, 0, 3, 0, 0, 15, + 128, 0, 0, 228, 176, 0, + 8, 228, 160, 1, 0, 0, + 2, 0, 8, 15, 128, 0, + 0, 228, 128, 255, 255, 0, + 0, 83, 72, 68, 82, 100, + 0, 0, 0, 64, 0, 0, + 0, 25, 0, 0, 0, 90, + 0, 0, 3, 0, 96, 16, + 0, 0, 0, 0, 0, 88, + 24, 0, 4, 0, 112, 16, + 0, 0, 0, 0, 0, 85, + 85, 0, 0, 98, 16, 0, + 3, 50, 16, 16, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 242, 32, 16, 0, 0, + 0, 0, 0, 69, 0, 0, + 9, 242, 32, 16, 0, 0, + 0, 0, 0, 70, 16, 16, + 0, 1, 0, 0, 0, 70, + 126, 16, 0, 0, 0, 0, + 0, 0, 96, 16, 0, 0, + 0, 0, 0, 62, 0, 0, + 1, 83, 84, 65, 84, 116, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, + 68, 69, 70, 156, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 28, 0, 0, 0, 0, + 4, 255, 255, 0, 129, 0, + 0, 105, 0, 0, 0, 92, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 2, + 0, 0, 0, 5, 0, 0, + 0, 4, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, + 0, 1, 0, 0, 0, 12, + 0, 0, 0, 115, 83, 97, + 109, 112, 108, 101, 114, 0, + 116, 101, 120, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 57, 46, 50, + 55, 46, 57, 53, 50, 46, + 51, 48, 50, 50, 0, 171, + 171, 73, 83, 71, 78, 80, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 3, 3, 0, 0, 83, + 86, 95, 80, 111, 115, 105, + 116, 105, 111, 110, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 171, 171, 171, 79, + 83, 71, 78, 44, 0, 0, + 0, 1, 0, 0, 0, 8, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 15, + 0, 0, 0, 83, 86, 95, + 84, 97, 114, 103, 101, 116, + 0, 171, 171, 135, 4, 0, + 0, 0, 0, 0, 0, 4, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 2, + 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 43, + 0, 0, 0, 15, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 52, 0, 0, + 0, 15, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 100, 0, 0, 0, 72, + 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, + 0, 0, 0, 145, 0, 0, + 0, 117, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, + 255, 3, 0, 0, 0, 55, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 100, + 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 154, 0, 0, + 0, 47, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 166, 0, 0, 0, 0, + 0, 0, 0, 178, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 192, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 7, + 0, 0, 0, 115, 4, 0, + 0, 8, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 123, 4, 0, 0, 7, + 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 71, + 7, 0, 0 +}; diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.fx b/gfx/cairo/cairo/src/cairo-d2d-private.fx new file mode 100644 index 000000000000..8f3236a4cda3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-d2d-private.fx @@ -0,0 +1,57 @@ +// We store vertex coordinates and the quad shape in a constant buffer, this is +// easy to update and allows us to use a single call to set the x, y, w, h of +// the quad. +// The QuadDesc and TexCoords both work as follows: +// The x component is the quad left point, the y component is the top point +// the z component is the width, and the w component is the height. The quad +// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f } +// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right +// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture +// space <0, 1.0f> left to right and top to bottom. The input vertices of the +// shader stage always form a rectangle from {0, 0} - {1, 1} +cbuffer cb0 +{ + float4 QuadDesc; + float4 TexCoords; +} + +struct VS_OUTPUT +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; +}; + +Texture2D tex; + +sampler sSampler = sampler_state { + Texture = tex; + AddressU = Clamp; + AddressV = Clamp; +}; + +VS_OUTPUT SampleTextureVS(float3 pos : POSITION) +{ + VS_OUTPUT Output; + Output.Position.w = 1.0f; + Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x; + Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y; + Output.Position.z = 0; + Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x; + Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y; + return Output; +} + +float4 SampleTexturePS( VS_OUTPUT In) : SV_Target +{ + return tex.Sample(sSampler, In.TexCoord); +}; + +technique10 SampleTexture +{ + pass P0 + { + SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); + SetGeometryShader(NULL); + SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS())); + } +} diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.h b/gfx/cairo/cairo/src/cairo-d2d-private.h index 3244bb42dbf9..da60e4168a1a 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-private.h +++ b/gfx/cairo/cairo/src/cairo-d2d-private.h @@ -49,10 +49,28 @@ extern "C" { } #include "cairo-win32-refptr.h" +#include "cairo-d2d-private-fx.h" +#include "cairo-win32.h" /* describes the type of the currently applied clip so that we can pop it */ struct d2d_clip; +#define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1 + +struct _cairo_d2d_device +{ + cairo_device_t base; + + HMODULE mD3D10_1; + RefPtr mD3D10Device; + RefPtr mSampleEffect; + RefPtr mInputLayout; + RefPtr mQuadBuffer; + RefPtr mRasterizerState; + RefPtr mBlendStates[MAX_OPERATORS]; +}; +typedef struct _cairo_d2d_device cairo_d2d_device_t; + struct _cairo_d2d_surface { _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false), textRenderingInit(true) @@ -61,6 +79,10 @@ struct _cairo_d2d_surface { } cairo_surface_t base; + /* Device used by this surface + * NOTE: In upstream cairo this is in the surface base class */ + cairo_d2d_device_t *device; + /** Render target of the texture we render to */ RefPtr rt; /** Surface containing our backstore */ @@ -103,6 +125,10 @@ struct _cairo_d2d_surface { /** Indicates if text rendering is initialized */ bool textRenderingInit; + RefPtr buffer_rt_view; + RefPtr buffer_sr_view; + + //cairo_surface_clipper_t clipper; }; typedef struct _cairo_d2d_surface cairo_d2d_surface_t; @@ -124,104 +150,14 @@ typedef HRESULT (WINAPI*D3D10CreateDevice1Func)( ID3D10Device1 **ppDevice ); -class D2DSurfFactory -{ -public: - static ID2D1Factory *Instance() - { - if (!mFactoryInstance) { - D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); - if (createD2DFactory) { - D2D1_FACTORY_OPTIONS options; -#ifdef DEBUG - options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; -#else - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; -#endif - createD2DFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory), - &options, - (void**)&mFactoryInstance); - } - } - return mFactoryInstance; - } -private: - static ID2D1Factory *mFactoryInstance; -}; - -/** - * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: - * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS - * can be misleading. In fact, that flag gives no such indication. I pointed this - * out to Bas in my email. However, Microsoft is in fact using this flag to - * indicate "light weight" DX applications. By light weight they are essentially - * referring to applications that are not games. The idea is that when you create - * a DX game, the driver assumes that you will pretty much have a single instance - * and therefore it doesn't try to hold back when it comes to GPU resource - * allocation as long as it can crank out performance. In other words, the - * priority in regular DX applications is to make that one application run as fast - * as you can. For "light weight" applications, including D2D applications, the - * priorities are a bit different. Now you are no longer going to have a single - * (or very few) instances. You can have a lot of them (say, for example, a - * separate DX context/device per browser tab). In such cases, the GPU resource - * allocation scheme changes. - */ -class D3D10Factory -{ -public: - static ID3D10Device1 *Device() - { - if (!mDeviceInstance) { - D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) - GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateDevice1"); - if (createD3DDevice) { - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_1, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - if (FAILED(hr)) { - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_0, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - if (FAILED(hr)) { - /* TODO: D3D10Level9 might be slower than GDI */ - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_9_3, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - - } - } - if (SUCCEEDED(hr)) { - mDeviceInstance->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); - } - } - } - return mDeviceInstance; - } -private: - static ID3D10Device1 *mDeviceInstance; -}; - +typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( + void *pData, + SIZE_T DataLength, + UINT FXFlags, + ID3D10Device *pDevice, + ID3D10EffectPool *pEffectPool, + ID3D10Effect **ppEffect +); RefPtr _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, @@ -229,8 +165,15 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, bool unique = false); void _cairo_d2d_begin_draw_state(cairo_d2d_surface_t *d2dsurf); + cairo_status_t _cairo_d2d_set_clip(cairo_d2d_surface_t *d2dsurf, cairo_clip_t *clip); +cairo_int_status_t _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL); + +RefPtr _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip); + +cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, const cairo_pattern_t *source); + #endif /* CAIRO_HAS_D2D_SURFACE */ #endif /* CAIRO_D2D_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index aa720ad8e898..71834be76bb0 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -47,11 +47,328 @@ extern "C" { // Required for using placement new. #include -ID2D1Factory *D2DSurfFactory::mFactoryInstance = NULL; -ID3D10Device1 *D3D10Factory::mDeviceInstance = NULL; - #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS +struct Vertex +{ + float position[2]; +}; + +// This factory is not device dependent, we can store it. But will clear it +// if there are no devices left needing it. +static ID2D1Factory *sD2DFactory = NULL; +static HMODULE sD2DModule; + +static void +_cairo_d2d_release_factory() +{ + int refcnt = sD2DFactory->Release(); + if (!refcnt) { + // Once the last reference goes, free the library. + sD2DFactory = NULL; + FreeLibrary(sD2DModule); + } +} + +/** + * Set a blending mode for an operator. This will also return a boolean that + * reports if for this blend mode the entire surface needs to be blended. This + * is true whenever the DEST blend is not ONE when src alpha is 0. + */ +static cairo_int_status_t +_cairo_d2d_set_operator(cairo_d2d_device_t *device, + cairo_operator_t op) +{ + assert(op < MAX_OPERATORS); + if (op >= MAX_OPERATORS) { + // Eep! Someone forgot to update MAX_OPERATORS probably. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (device->mBlendStates[op]) { + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; + } + + D3D10_BLEND_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.BlendEnable[0] = TRUE; + desc.AlphaToCoverageEnable = FALSE; + desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; + + switch (op) { + case CAIRO_OPERATOR_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_ADD: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_XOR: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + }; + device->mD3D10Device->CreateBlendState(&desc, &device->mBlendStates[op]); + + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_device_t * +cairo_d2d_create_device() +{ + D3D10_RASTERIZER_DESC rastDesc; + D3D10_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + D3D10_PASS_DESC passDesc; + ID3D10EffectTechnique *technique; + Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; + CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); + D3D10_SUBRESOURCE_DATA data; + + cairo_d2d_device_t *device = new cairo_d2d_device_t; + device->mD3D10_1 = LoadLibraryA("d3d10_1.dll"); + D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) + GetProcAddress(device->mD3D10_1, "D3D10CreateDevice1"); + D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) + GetProcAddress(device->mD3D10_1, "D3D10CreateEffectFromMemory"); + D2D1CreateFactoryFunc createD2DFactory; + + if (!createD3DDevice || !createEffect) { + goto FAILED; + } + + /** + * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: + * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS + * can be misleading. In fact, that flag gives no such indication. I pointed this + * out to Bas in my email. However, Microsoft is in fact using this flag to + * indicate "light weight" DX applications. By light weight they are essentially + * referring to applications that are not games. The idea is that when you create + * a DX game, the driver assumes that you will pretty much have a single instance + * and therefore it doesn't try to hold back when it comes to GPU resource + * allocation as long as it can crank out performance. In other words, the + * priority in regular DX applications is to make that one application run as fast + * as you can. For "light weight" applications, including D2D applications, the + * priorities are a bit different. Now you are no longer going to have a single + * (or very few) instances. You can have a lot of them (say, for example, a + * separate DX context/device per browser tab). In such cases, the GPU resource + * allocation scheme changes. + */ + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_1, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + if (FAILED(hr)) { + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_0, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + if (FAILED(hr)) { + /* This is not guaranteed to be too fast! */ + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_9_3, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + + } + } + if (FAILED(hr)) { + goto FAILED; + } + + if (!sD2DFactory) { + sD2DModule = LoadLibraryW(L"d2d1.dll"); + createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(sD2DModule, "D2D1CreateFactory"); + if (!createD2DFactory) { + goto FAILED; + } + D2D1_FACTORY_OPTIONS options; +#ifdef DEBUG + options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; +#else + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; +#endif + hr = createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&sD2DFactory); + if (FAILED(hr)) { + goto FAILED; + } + } else { + sD2DFactory->AddRef(); + } + + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); + createEffect((void*)g_main, sizeof(g_main), 0, device->mD3D10Device, NULL, &device->mSampleEffect); + + technique = device->mSampleEffect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->GetDesc(&passDesc); + + + hr = device->mD3D10Device->CreateInputLayout(layout, + sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), + passDesc.pIAInputSignature, + passDesc.IAInputSignatureSize, + &device->mInputLayout); + if (FAILED(hr)) { + goto FAILED; + } + + data.pSysMem = (void*)vertices; + hr = device->mD3D10Device->CreateBuffer(&bufferDesc, &data, &device->mQuadBuffer); + if (FAILED(hr)) { + goto FAILED; + } + + memset(&rastDesc, 0, sizeof(rastDesc)); + rastDesc.CullMode = D3D10_CULL_NONE; + rastDesc.FillMode = D3D10_FILL_SOLID; + hr = device->mD3D10Device->CreateRasterizerState(&rastDesc, &device->mRasterizerState); + if (FAILED(hr)) { + goto FAILED; + } + device->base.refcount = 1; + + return &device->base; +FAILED: + delete &device->base; + return NULL; +} + +int +cairo_release_device(cairo_device_t *device) +{ + int newrefcnt = --device->refcount; + if (!newrefcnt) { + // Call the correct destructor + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + FreeLibrary(d2d_device->mD3D10_1); + delete d2d_device; + _cairo_d2d_release_factory(); + } + return newrefcnt; +} + +int +cairo_addref_device(cairo_device_t *device) +{ + return ++device->refcount; +} + +void +cairo_d2d_finish_device(cairo_device_t *device) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + // Here it becomes interesting, this flush method is generally called when + // interop is going on between our device and another device. The + // synchronisation between these devices is not always that great. The + // device flush method may flush the device's command queue, but it gives + // no guarantee that the device will actually be done with those commands, + // and so the surface may still not be complete when the external device + // chooses to use it. The EVENT query will actually tell us when the GPU + // is completely done with our commands. + D3D10_QUERY_DESC queryDesc; + queryDesc.MiscFlags = 0; + queryDesc.Query = D3D10_QUERY_EVENT; + RefPtr query; + + d2d_device->mD3D10Device->CreateQuery(&queryDesc, &query); + + // QUERY_EVENT does not use Begin(). It's disabled. + query->End(); + + BOOL done = FALSE; + while (!done) { + // This will return S_OK and done = FALSE when the GPU is not done, and + // S_OK and done = TRUE when the GPU is done. Any other return value + // means we need to break out or risk an infinite loop. + if (FAILED(query->GetData(&done, sizeof(BOOL), 0))) { + break; + } + } +} + +static void +_cairo_d2d_setup_for_blend(cairo_d2d_device_t *device) +{ + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device->mD3D10Device->IASetInputLayout(device->mInputLayout); + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = device->mQuadBuffer; + device->mD3D10Device->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + + device->mD3D10Device->RSSetState(device->mRasterizerState); +} + // Contains our cache usage - perhaps this should be made threadsafe. static int cache_usage = 0; @@ -347,6 +664,15 @@ _cairo_d2d_color_from_cairo_color(const cairo_color_t &color) (FLOAT)color.alpha); } +static void +_cairo_d2d_round_out_to_int_rect(cairo_rectangle_int_t *rect, double x1, double y1, double x2, double y2) +{ + rect->x = (int)floor(x1); + rect->y = (int)floor(y1); + rect->width = (int)ceil(x2) - rect->x; + rect->height = (int)ceil(y2) - rect->y; +} + /** * Gets the surface buffer texture for window surfaces whose backbuffer * is not directly usable as a bitmap. @@ -366,7 +692,7 @@ _cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface) softDesc.MipLevels = 1; softDesc.Usage = D3D10_USAGE_DEFAULT; softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); + surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); } return surface->bufferTexture; } @@ -414,7 +740,7 @@ void cairo_d2d_present_backbuffer(cairo_surface_t *surface) _cairo_d2d_flush(d2dsurf); if (d2dsurf->dxgiChain) { d2dsurf->dxgiChain->Present(0, 0); - D3D10Factory::Device()->Flush(); + d2dsurf->device->mD3D10Device->Flush(); } } @@ -743,16 +1069,16 @@ _cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style } RefPtr strokeStyle; - D2DSurfFactory::Instance()->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, - line_cap, - line_cap, - line_join, - (FLOAT)style->miter_limit, - dashStyle, - (FLOAT)style->dash_offset), - dashes, - style->num_dashes, - &strokeStyle); + sD2DFactory->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, + line_cap, + line_cap, + line_join, + (FLOAT)style->miter_limit, + dashStyle, + (FLOAT)style->dash_offset), + dashes, + style->num_dashes, + &strokeStyle); delete [] dashes; return strokeStyle; } @@ -769,6 +1095,18 @@ cairo_user_data_key_t bitmap_key_extend; cairo_user_data_key_t bitmap_key_snapshot; struct cached_bitmap { + cached_bitmap() + { + sD2DFactory->AddRef(); + } + + ~cached_bitmap() + { + // Clear bitmap out first because it depends on the factory. + bitmap = NULL; + _cairo_d2d_release_factory(); + } + /** The cached bitmap */ RefPtr bitmap; /** The cached bitmap is dirty and needs its data refreshed */ @@ -1724,7 +2062,7 @@ _cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, D2D1_FIGURE_BEGIN type) { RefPtr d2dpath; - D2DSurfFactory::Instance()->CreatePathGeometry(&d2dpath); + sD2DFactory->CreatePathGeometry(&d2dpath); RefPtr sink; d2dpath->Open(&sink); D2D1_FILL_MODE fillMode = D2D1_FILL_MODE_WINDING; @@ -1844,8 +2182,8 @@ _cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf, return CAIRO_INT_STATUS_SUCCESS; } -static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, - const cairo_pattern_t *source) +cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, + const cairo_pattern_t *source) { if (op == CAIRO_OPERATOR_SOURCE) { /** Operator over is easier for D2D! If the source if opaque, change */ @@ -1935,7 +2273,7 @@ _cairo_d2d_create_similar(void *surface, RefPtr texture; RefPtr dxgiSurface; - hr = D3D10Factory::Device()->CreateTexture2D(&desc, NULL, &texture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); if (FAILED(hr)) { goto FAIL_CREATESIMILAR; } @@ -1952,14 +2290,14 @@ _cairo_d2d_create_similar(void *surface, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE : D2D1_RENDER_TARGET_USAGE_NONE; - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(dxgiSurface, - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha), - dpiX, - dpiY, - usage), - &newSurf->rt); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha), + dpiX, + dpiY, + usage), + &newSurf->rt); if (FAILED(hr)) { goto FAIL_CREATESIMILAR; @@ -1982,6 +2320,9 @@ _cairo_d2d_create_similar(void *surface, _d2d_clear_surface(newSurf); + newSurf->device = d2dsurf->device; + cairo_addref_device(&newSurf->device->base); + return reinterpret_cast(newSurf); FAIL_CREATESIMILAR: @@ -1998,6 +2339,7 @@ _cairo_d2d_finish(void *surface) reset_clip(d2dsurf); + cairo_release_device(&d2dsurf->device->base); d2dsurf->~cairo_d2d_surface_t(); return CAIRO_STATUS_SUCCESS; } @@ -2032,12 +2374,12 @@ _cairo_d2d_acquire_source_image(void *abstract_surface, softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; - hr = D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &softTexture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); if (FAILED(hr)) { return CAIRO_STATUS_NO_MEMORY; } - D3D10Factory::Device()->CopyResource(softTexture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); D3D10_MAPPED_TEXTURE2D data; hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); @@ -2105,11 +2447,11 @@ _cairo_d2d_acquire_dest_image(void *abstract_surface, softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; - hr = D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &softTexture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); if (FAILED(hr)) { return CAIRO_STATUS_NO_MEMORY; } - D3D10Factory::Device()->CopyResource(softTexture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); D3D10_MAPPED_TEXTURE2D data; hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); @@ -2149,7 +2491,7 @@ _cairo_d2d_release_dest_image(void *abstract_surface, cairo_surface_destroy(&image->base); softTexture->Unmap(0); - D3D10Factory::Device()->CopyResource(d2dsurf->surface, softTexture); + d2dsurf->device->mD3D10Device->CopyResource(d2dsurf->surface, softTexture); softTexture->Release(); } @@ -2168,7 +2510,342 @@ _cairo_d2d_flush(void *surface) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, + cairo_d2d_surface_t *src, + cairo_point_int_t *translation, + cairo_region_t *region) +{ + RefPtr dstSurface; + dst->surface->QueryInterface(&dstSurface); + RefPtr srcSurface; + src->surface->QueryInterface(&srcSurface); + DXGI_SURFACE_DESC srcDesc, dstDesc; + srcSurface->GetDesc(&srcDesc); + dstSurface->GetDesc(&dstDesc); + + cairo_rectangle_int_t clip_rect; + clip_rect.x = 0; + clip_rect.y = 0; + clip_rect.width = dstDesc.Width; + clip_rect.height = dstDesc.Height; + + cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; + + _cairo_d2d_flush(dst); + ID3D10Resource *srcResource = src->surface; + if (src->surface.get() == dst->surface.get()) { + // Self-copy + srcResource = _cairo_d2d_get_buffer_texture(dst); + src->device->mD3D10Device->CopyResource(srcResource, src->surface); + } else { + // Need to flush the source too if it's a different surface. + _cairo_d2d_flush(src); + } + + // One copy for each rectangle in the final clipping region. + for (int i = 0; i < cairo_region_num_rectangles(region); i++) { + D3D10_BOX rect; + cairo_rectangle_int_t area_to_copy; + + cairo_region_get_rectangle(region, i, &area_to_copy); + + cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x, + area_to_copy.y + translation->y, + area_to_copy.width, area_to_copy.height }; + cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height }; + + + if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) { + /* We cannot do any sort of extend, in the future a little bit of extra code could + * allow us to support EXTEND_NONE. + */ + rv = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + rect.front = 0; + rect.back = 1; + rect.left = transformed_rect.x; + rect.top = transformed_rect.y; + rect.right = transformed_rect.x + transformed_rect.width; + rect.bottom = transformed_rect.y + transformed_rect.height; + + src->device->mD3D10Device->CopySubresourceRegion(dst->surface, + 0, + area_to_copy.x, + area_to_copy.y, + 0, + srcResource, + 0, + &rect); + } + + return rv; +} + +/** + * This function will text if we can use GPU mem cpy to execute an operation with + * a surface pattern. If box is NULL it will operate on the entire dst surface. + */ +static cairo_int_status_t +_cairo_d2d_try_copy(cairo_d2d_surface_t *dst, + cairo_surface_t *src, + cairo_box_t *box, + const cairo_matrix_t *matrix, + cairo_clip_t *clip, + cairo_operator_t op) +{ + if (op != CAIRO_OPERATOR_SOURCE && + !(op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_point_int_t translation; + if ((box && !box_is_integer(box)) || + !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* For now we do only D2D sources */ + if (src->type != CAIRO_SURFACE_TYPE_D2D) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_rectangle_int_t rect; + if (box) { + _cairo_box_round_to_rectangle(box, &rect); + } else { + rect.x = rect.y = 0; + rect.width = dst->rt->GetPixelSize().width; + rect.height = dst->rt->GetPixelSize().height; + } + + cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); + + if (d2dsrc->device != dst->device) { + // This doesn't work between different devices. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* Region we need to clip this operation to */ + cairo_region_t *clipping_region = NULL; + cairo_region_t *region; + if (clip) { + _cairo_clip_get_region(clip, &clipping_region); + + if (!clipping_region) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + region = cairo_region_copy(clipping_region); + + cairo_region_intersect_rectangle(region, &rect); + + if (cairo_region_is_empty(region)) { + // Nothing to do. + return CAIRO_INT_STATUS_SUCCESS; + } + } else { + region = cairo_region_create_rectangle(&rect); + // Areas outside of the surface do not matter. + cairo_rectangle_int_t surface_rect = { 0, 0, + dst->rt->GetPixelSize().width, + dst->rt->GetPixelSize().height }; + cairo_region_intersect_rectangle(region, &surface_rect); + } + + cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region); + + cairo_region_destroy(region); + + return rv; +} + +RefPtr _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip) +{ + RefPtr texture = _cairo_d2d_get_buffer_texture(surf); + RefPtr new_rt; + RefPtr dxgiSurface; + texture->QueryInterface(&dxgiSurface); + HRESULT hr; + + _cairo_d2d_flush(surf); + + if (!surf) { + return NULL; + } + + D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &new_rt); + + if (FAILED(hr)) { + return NULL; + } + + new_rt->BeginDraw(); + new_rt->Clear(D2D1::ColorF(0, 0)); + + // Since this is a fresh surface there's no point in doing clever things to + // keep the clip path around until a certain depth. So we just do a straight- + // forward push of all clip paths in the tree, similar to what the normal + // clip code does, but a little less clever. + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + cairo_box_t clip_box; + if (_cairo_path_fixed_is_box(&path->path, &clip_box)) { + // If this does not have a region it could be none-pixel aligned. + D2D1_ANTIALIAS_MODE aaMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + if (box_is_integer(&clip_box)) { + aaMode = D2D1_ANTIALIAS_MODE_ALIASED; + } + new_rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(clip_box.p1.x), + _cairo_fixed_to_float(clip_box.p1.y), + _cairo_fixed_to_float(clip_box.p2.x), + _cairo_fixed_to_float(clip_box.p2.y)), + aaMode); + } else { + HRESULT hr; + RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&path->path, + path->fill_rule, + D2D1_FIGURE_BEGIN_FILLED); + RefPtr layer; + + hr = new_rt->CreateLayer (&layer); + + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + + new_rt->PushLayer(D2D1::LayerParameters( + D2D1::InfiniteRect(), + geom, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0, + 0, + options), + layer); + } + path = path->prev; + } + } + return new_rt; +} + +cairo_int_status_t _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds) +{ + int numPaths = 0; + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + numPaths++; + path = path->prev; + } + + cairo_clip_path_t **paths = new cairo_clip_path_t*[numPaths]; + + numPaths = 0; + path = clip->path; + while (path) { + paths[numPaths++] = path; + path = path->prev; + } + + for (int i = numPaths - 1; i >= 0; i--) { + if (paths[i]->flags & CAIRO_CLIP_PATH_IS_BOX) { + rt->PopAxisAlignedClip(); + } else { + rt->PopLayer(); + } + } + delete [] paths; + } + rt->EndDraw(); + HRESULT hr; + + RefPtr srcTexture = _cairo_d2d_get_buffer_texture(surf); + RefPtr dstTexture; + + surf->surface->QueryInterface(&dstTexture); + ID3D10Device *device = surf->device->mD3D10Device; + + if (!surf->buffer_rt_view) { + hr = device->CreateRenderTargetView(dstTexture, NULL, &surf->buffer_rt_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (!surf->buffer_sr_view) { + hr = device->CreateShaderResourceView(srcTexture, NULL, &surf->buffer_sr_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + cairo_int_status_t status; + + status = _cairo_d2d_set_operator(surf->device, op); + + if (unlikely(status)) { + return status; + } + + D3D10_TEXTURE2D_DESC tDesc; + dstTexture->GetDesc(&tDesc); + D3D10_VIEWPORT vp; + vp.Height = tDesc.Height; + vp.MinDepth = 0; + vp.MaxDepth = 1.0; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = tDesc.Width; + device->RSSetViewports(1, &vp); + + ID3D10Effect *effect = surf->device->mSampleEffect; + + ID3D10RenderTargetView *rtViewPtr = surf->buffer_rt_view; + device->OMSetRenderTargets(1, &rtViewPtr, 0); + ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); + ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); + + float quadDescVal[] = { -1.0f, 1.0f, 2.0f, -2.0f }; + float texCoordsVal[] = { 0.0, 0.0, 1.0f, 1.0f }; + if (bounds && _cairo_operator_bounded_by_mask(op)) { + quadDescVal[0] = -1.0f + ((float)bounds->x / (float)tDesc.Width) * 2.0f; + quadDescVal[1] = 1.0f - ((float)bounds->y / (float)tDesc.Height) * 2.0f; + quadDescVal[2] = ((float)bounds->width / (float)tDesc.Width) * 2.0f; + quadDescVal[3] = -((float)bounds->height / (float)tDesc.Height) * 2.0f; + texCoordsVal[0] = (float)bounds->x / (float)tDesc.Width; + texCoordsVal[1] = (float)bounds->y / (float)tDesc.Height; + texCoordsVal[2] = (float)bounds->width / (float)tDesc.Width; + texCoordsVal[3] = (float)bounds->height / (float)tDesc.Height; + } + quadDesc->SetFloatVector(quadDescVal); + texCoords->SetFloatVector(texCoordsVal); + + _cairo_d2d_setup_for_blend(surf->device); + ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->Apply(0); + + ID3D10ShaderResourceView *srViewPtr = surf->buffer_sr_view; + device->PSSetShaderResources(0, 1, &srViewPtr); + + device->Draw(4, 0); + +#ifdef DEBUG + // Quiet down some info messages from D3D10 debug layer + srViewPtr = NULL; + device->PSSetShaderResources(0, 1, &srViewPtr); + rtViewPtr = NULL; + device->OMSetRenderTargets(1, &rtViewPtr, 0); +#endif + return CAIRO_INT_STATUS_SUCCESS; +} static cairo_int_status_t _cairo_d2d_paint(void *surface, @@ -2181,18 +2858,44 @@ _cairo_d2d_paint(void *surface, op = _cairo_d2d_simplify_operator(op, source); + if (op == CAIRO_OPERATOR_SOURCE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (op == CAIRO_OPERATOR_CLEAR) { return _cairo_d2d_clear(d2dsurf, clip); } + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + status = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + NULL, &source->matrix, clip, op); - if (unlikely(status)) - return status; + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + return status; + } + } + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + if (unlikely(status)) + return status; + } +#endif + + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source); @@ -2200,23 +2903,16 @@ _cairo_d2d_paint(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - if (op == CAIRO_OPERATOR_OVER) { - D2D1_SIZE_F size = d2dsurf->rt->GetSize(); - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - } else if (op == CAIRO_OPERATOR_SOURCE) { - D2D1_SIZE_F size = d2dsurf->rt->GetSize(); - d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; + + D2D1_SIZE_F size = target_rt->GetSize(); + target_rt->FillRectangle(D2D1::RectF((FLOAT)0, + (FLOAT)0, + (FLOAT)size.width, + (FLOAT)size.height), + brush); + + if (target_rt.get() != d2dsurf->rt.get()) { + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); } return CAIRO_INT_STATUS_SUCCESS; @@ -2317,27 +3013,32 @@ _cairo_d2d_stroke(void *surface, op = _cairo_d2d_simplify_operator(op, source); - if (op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD) { - /** - * We don't really support ADD yet. True ADD support requires getting - * the tesselated mesh from D2D, and blending that using D3D which has - * an add operator available. - */ + if (op == CAIRO_OPERATOR_SOURCE) { return CAIRO_INT_STATUS_UNSUPPORTED; } - _begin_draw_state(d2dsurf); - - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely (status)) - return status; + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + if (unlikely(status)) + return status; + } +#endif if (antialias == CAIRO_ANTIALIAS_NONE) { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); } else { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } RefPtr strokeStyle = _cairo_d2d_create_strokestyle_for_stroke_style(style); @@ -2351,9 +3052,9 @@ _cairo_d2d_stroke(void *surface, D2D1::Matrix3x2F inverse_mat = _cairo_d2d_invert_matrix(mat); RefPtr trans_geom; - D2DSurfFactory::Instance()->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); + sD2DFactory->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); - d2dsurf->rt->SetTransform(mat); + target_rt->SetTransform(mat); RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source); @@ -2361,9 +3062,18 @@ _cairo_d2d_stroke(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->DrawGeometry(trans_geom, brush, (FLOAT)style->line_width, strokeStyle); + target_rt->DrawGeometry(trans_geom, brush, (FLOAT)style->line_width, strokeStyle); + + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); + + if (target_rt.get() != d2dsurf->rt.get()) { + D2D1_RECT_F bounds; + trans_geom->GetWidenedBounds((FLOAT)style->line_width, strokeStyle, D2D1::IdentityMatrix(), &bounds); + cairo_rectangle_int_t bound_rect; + _cairo_d2d_round_out_to_int_rect(&bound_rect, bounds.left, bounds.top, bounds.right, bounds.bottom); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bound_rect); + } - d2dsurf->rt->SetTransform(D2D1::Matrix3x2F::Identity()); return CAIRO_INT_STATUS_SUCCESS; } @@ -2380,43 +3090,66 @@ _cairo_d2d_fill(void *surface, cairo_int_status_t status; cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_box_t box; + bool is_box = _cairo_path_fixed_is_box(path, &box); + + if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + cairo_int_status_t rv = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + &box, &source->matrix, clip, op); + + if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { + return rv; + } + } op = _cairo_d2d_simplify_operator(op, source); - if (op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD && - op != CAIRO_OPERATOR_CLEAR) { - /** - * We don't really support ADD yet. True ADD support requires getting - * the tesselated mesh from D2D, and blending that using D3D which has - * an add operator available. - */ + if (op == CAIRO_OPERATOR_SOURCE) { return CAIRO_INT_STATUS_UNSUPPORTED; } - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - - - if (antialias == CAIRO_ANTIALIAS_NONE) { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - } else { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } - - cairo_box_t box; - if (op == CAIRO_OPERATOR_CLEAR) { if (_cairo_path_fixed_is_box(path, &box)) { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + return _cairo_d2d_clear_box (d2dsurf, clip, &box); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } } - if (_cairo_path_fixed_is_box(path, &box)) { + RefPtr target_rt = d2dsurf->rt; + +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + } +#endif + + if (antialias == CAIRO_ANTIALIAS_NONE) { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + } else { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + + if (is_box) { float x1 = _cairo_fixed_to_float(box.p1.x); float y1 = _cairo_fixed_to_float(box.p1.y); float x2 = _cairo_fixed_to_float(box.p2.x); @@ -2427,11 +3160,11 @@ _cairo_d2d_fill(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->FillRectangle(D2D1::RectF(x1, - y1, - x2, - y2), - brush); + target_rt->FillRectangle(D2D1::RectF(x1, + y1, + x2, + y2), + brush); } else { RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED); @@ -2440,8 +3173,17 @@ _cairo_d2d_fill(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->FillGeometry(d2dpath, brush); + target_rt->FillGeometry(d2dpath, brush); } + + if (target_rt.get() != d2dsurf->rt.get()) { + double x1, y1, x2, y2; + _cairo_path_fixed_bounds(path, &x1, &y1, &x2, &y2); + cairo_rectangle_int_t bounds; + _cairo_d2d_round_out_to_int_rect(&bounds, x1, y1, x2, y2); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bounds); + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -2493,17 +3235,11 @@ _cairo_d2d_getextents(void *surface, /** Helper functions. */ cairo_surface_t* -cairo_d2d_surface_create_for_hwnd(HWND wnd, +cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device, + HWND wnd, cairo_content_t content) { - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - /** - * FIXME: In the near future we can use cairo_device_t to pass in a - * device. - */ - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } - + cairo_d2d_device_t *d2d_device = reinterpret_cast(cairo_device); cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); new (newSurf) cairo_d2d_surface_t(); @@ -2533,7 +3269,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, if (!sizePixels.height) { sizePixels.height = 1; } - ID3D10Device1 *device = D3D10Factory::Device(); + ID3D10Device1 *device = d2d_device->mD3D10Device; RefPtr dxgiDevice; RefPtr dxgiAdapter; RefPtr dxgiFactory; @@ -2595,7 +3331,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, dpiX, dpiY, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE); - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, props, &newSurf->rt); if (FAILED(hr)) { @@ -2609,6 +3345,9 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, _d2d_clear_surface(newSurf); + newSurf->device = d2d_device; + cairo_addref_device(cairo_device); + return reinterpret_cast(newSurf); FAIL_HWND: @@ -2618,17 +3357,12 @@ FAIL_HWND: } cairo_surface_t * -cairo_d2d_surface_create(cairo_format_t format, +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, int width, int height) { - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - /** - * FIXME: In the near future we can use cairo_device_t to pass in a - * device. - */ - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); new (newSurf) cairo_d2d_surface_t(); @@ -2671,7 +3405,7 @@ cairo_d2d_surface_create(cairo_format_t format, D2D1_BITMAP_PROPERTIES bitProps; D2D1_RENDER_TARGET_PROPERTIES props; - hr = D3D10Factory::Device()->CreateTexture2D(&desc, NULL, &texture); + hr = d2d_device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); if (FAILED(hr)) { goto FAIL_CREATE; @@ -2691,7 +3425,7 @@ cairo_d2d_surface_create(cairo_format_t format, if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(dxgiSurface, + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, props, &newSurf->rt); @@ -2720,6 +3454,9 @@ cairo_d2d_surface_create(cairo_format_t format, _d2d_clear_surface(newSurf); + newSurf->device = d2d_device; + cairo_addref_device(device); + return reinterpret_cast(newSurf); FAIL_CREATE: @@ -2728,6 +3465,108 @@ FAIL_CREATE: return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); } +cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content) +{ + if (!device) { + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); + } + + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + HRESULT hr; + RefPtr texture; + RefPtr dxgiSurface; + D2D1_BITMAP_PROPERTIES bitProps; + D2D1_RENDER_TARGET_PROPERTIES props; + DXGI_FORMAT format; + DXGI_SURFACE_DESC desc; + + hr = d2d_device->mD3D10Device->OpenSharedResource(handle, + __uuidof(ID3D10Resource), + (void**)&newSurf->surface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + hr = newSurf->surface->QueryInterface(&dxgiSurface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + dxgiSurface->GetDesc(&desc); + format = desc.Format; + + D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + if (format == DXGI_FORMAT_B8G8R8A8_UNORM) { + if (content == CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, content); + if (content == CAIRO_CONTENT_COLOR) { + alpha = D2D1_ALPHA_MODE_IGNORE; + } + } else if (format == DXGI_FORMAT_A8_UNORM) { + if (content != CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, CAIRO_CONTENT_ALPHA); + } else { + status = CAIRO_STATUS_INVALID_FORMAT; + // We don't know how to support this format! + goto FAIL_CREATEHANDLE; + } + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (format != DXGI_FORMAT_A8_UNORM) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _d2d_clear_surface(newSurf); + + newSurf->device = d2d_device; + cairo_addref_device(device); + + return &newSurf->base; + +FAIL_CREATEHANDLE: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(status)); +} + void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip) { if (surface->type != CAIRO_SURFACE_TYPE_D2D) { @@ -2778,8 +3617,8 @@ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t } ID3D10Texture2D *texture = _cairo_d2d_get_buffer_texture(d2dsurf); - D3D10Factory::Device()->CopyResource(texture, d2dsurf->surface); - D3D10Factory::Device()->CopySubresourceRegion(d2dsurf->surface, + d2dsurf->device->mD3D10Device->CopyResource(texture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopySubresourceRegion(d2dsurf->surface, 0, point.x, point.y, @@ -2790,19 +3629,6 @@ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t } -cairo_bool_t -cairo_d2d_has_support() -{ - /** - * FIXME: We should be able to fix this in the near future when we pass in - * a cairo_device_t to our surface creation functions. - */ - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - return false; - } - return true; -} - HDC cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents) { diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp index 271f5e321d4b..238539b1938a 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp +++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp @@ -1262,32 +1262,52 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; + op = _cairo_d2d_simplify_operator(op, source); - /* We can only handle operator SOURCE or OVER with the destination - * having no alpha */ - if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) - return CAIRO_INT_STATUS_UNSUPPORTED; + /* We cannot handle operator SOURCE or CLEAR */ + if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } - _cairo_d2d_begin_draw_state (dst); - _cairo_d2d_set_clip (dst, clip); + RefPtr target_rt = dst->rt; + cairo_rectangle_int_t fontArea; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(dst, clip); + + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _cairo_d2d_begin_draw_state(dst); + status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip); + + if (unlikely(status)) + return status; + } +#endif - D2D1_TEXT_ANTIALIAS_MODE cleartype = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + D2D1_TEXT_ANTIALIAS_MODE highest_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - if (dst->base.content != CAIRO_CONTENT_COLOR) { - cleartype = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + // If we're rendering to a temporary surface we cannot do sub-pixel AA. + if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) { + highest_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; } + switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_DEFAULT: - dst->rt->SetTextAntialiasMode(cleartype); + target_rt->SetTextAntialiasMode(highest_quality); break; case CAIRO_ANTIALIAS_NONE: - dst->rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); + target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); break; case CAIRO_ANTIALIAS_GRAY: - dst->rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); + target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); break; case CAIRO_ANTIALIAS_SUBPIXEL: - dst->rt->SetTextAntialiasMode(cleartype); + target_rt->SetTextAntialiasMode(highest_quality); break; } @@ -1349,7 +1369,29 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, D2D1::Matrix3x2F mat = _cairo_d2d_matrix_from_matrix(&dwritesf->mat); if (transform) { - dst->rt->SetTransform(mat); + target_rt->SetTransform(mat); + } + + if (dst->rt.get() != target_rt.get()) { + RefPtr analysis; + DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + transform ? &dwmat : 0, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0, + 0, + &analysis); + + RECT bounds; + analysis->GetAlphaTextureBounds(scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE ? + DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, + &bounds); + fontArea.x = bounds.left; + fontArea.y = bounds.top; + fontArea.width = bounds.right - bounds.left; + fontArea.height = bounds.bottom - bounds.top; } RefPtr brush = _cairo_d2d_create_brush_for_pattern(dst, @@ -1374,15 +1416,19 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, brush->SetTransform(&mat_brush); } - dst->rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush); + target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush); if (transform) { - dst->rt->SetTransform(D2D1::Matrix3x2F::Identity()); + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); } delete [] indices; delete [] offsets; delete [] advances; + + if (target_rt.get() != dst->rt.get()) { + return _cairo_d2d_blend_temp_surface(dst, op, target_rt, clip, &fontArea); + } return CAIRO_INT_STATUS_SUCCESS; } diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c index e887c75134aa..40a8bc95c4ba 100644 --- a/gfx/cairo/cairo/src/cairo-rectangle.c +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -94,6 +94,26 @@ _cairo_boxes_get_extents (const cairo_box_t *boxes, } } +/* This function will return 'true' if the containing_rectangle contains the + * contained_rectangle, and false otherwise. + */ +cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle) +{ + if (containing_rectangle->x > contained_rectangle->x || + containing_rectangle->y > contained_rectangle->y) + return FALSE; + + if (containing_rectangle->x + containing_rectangle->width < + contained_rectangle->x + contained_rectangle->width || + containing_rectangle->y + containing_rectangle->height < + contained_rectangle->y + contained_rectangle->height) + return FALSE; + + return TRUE; +} + /* XXX We currently have a confusing mix of boxes and rectangles as * exemplified by this function. A #cairo_box_t is a rectangular area * represented by the coordinates of the upper left and lower right diff --git a/gfx/cairo/cairo/src/cairo-win32-refptr.h b/gfx/cairo/cairo/src/cairo-win32-refptr.h index 11fc0252eea5..6dd5ac323431 100644 --- a/gfx/cairo/cairo/src/cairo-win32-refptr.h +++ b/gfx/cairo/cairo/src/cairo-win32-refptr.h @@ -107,6 +107,11 @@ public: return mPtr; } + T* operator->() const + { + return mPtr; + } + operator bool() { return (mPtr ? true : false); diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h index 53fed4d03ef7..e6e7feea0106 100644 --- a/gfx/cairo/cairo/src/cairo-win32.h +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -126,30 +126,88 @@ cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrit #endif /* CAIRO_HAS_DWRITE_FONT */ #if CAIRO_HAS_D2D_SURFACE + +struct _cairo_device +{ + int type; + int refcount; +}; +typedef struct _cairo_device cairo_device_t; + +/** + * Create a D2D device + * + * \return New D2D device, NULL if creation failed. + */ +cairo_device_t * +cairo_d2d_create_device(); + +/** + * Releases a D2D device. + * + * \return References left to the device + */ +int +cairo_release_device(cairo_device_t *device); + +/** + * Addrefs a D2D device. + * + * \return References to the device + */ +int +cairo_addref_device(cairo_device_t *device); + +/** + * Flushes a D3D device. In most cases the surface backend will do this + * internally, but when using a surfaces created from a shared handle this + * should be executed manually when a different device is going to be accessing + * the same surface data. This will also block until the device is finished + * processing all work. + */ +void +cairo_d2d_finish_device(cairo_device_t *device); /** * Create a D2D surface for an HWND * + * \param device Device used to create the surface * \param wnd Handle for the window * \param content Content of the window, should be COLOR_ALPHA for transparent windows * \return New cairo surface */ cairo_public cairo_surface_t * -cairo_d2d_surface_create_for_hwnd(HWND wnd, cairo_content_t content); +cairo_d2d_surface_create_for_hwnd(cairo_device_t *device, HWND wnd, cairo_content_t content); /** * Create a D2D surface of a certain size. * + * \param device Device used to create the surface * \param format Cairo format of the surface * \param width Width of the surface * \param height Height of the surface * \return New cairo surface */ cairo_public cairo_surface_t * -cairo_d2d_surface_create(cairo_format_t format, +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, int width, int height); +/** + * Create a D3D surface from a Texture SharedHandle, this is obtained from a + * CreateTexture call on a D3D9 device. This has to be an A8R8G8B8 format + * or an A8 format, the treatment of the alpha channel can be indicated using + * the content parameter. + * + * \param device Device used to create the surface + * \param handle Shared handle to the texture we want to wrap + * \param content Content of the texture, COLOR_ALPHA for ARGB + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content); + /** * Present the backbuffer for a surface create for an HWND. This needs * to be called when the owner of the original window surface wants to @@ -172,15 +230,6 @@ void cairo_d2d_present_backbuffer(cairo_surface_t *surface); */ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip); -/** - * Verify if D2D surfaces are actually supported. This will confirm the needed - * hardware is available. - * - * \return True if the support is available. If false surface creation will - * return error surfaces. - */ -cairo_bool_t cairo_d2d_has_support(); - /** * Get a DC for the current render target. When selecting the retention option this * call can be relatively slow, since it may require reading back contents from the diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 28861d73ef4b..63612e63ae35 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -271,6 +271,10 @@ cairo_private void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rectangle); +cairo_private cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle); + cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle); diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 2f397ca30235..9ea382af17eb 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -84,7 +84,10 @@ CPPSRCS = \ ifeq ($(MOZ_WIDGET_TOOLKIT),windows) ifdef MOZ_ENABLE_D3D9_LAYER -EXPORTS += LayerManagerD3D9.h +EXPORTS += \ + LayerManagerD3D9.h \ + DeviceManagerD3D9.h \ + $(NULL) CPPSRCS += \ LayerManagerD3D9.cpp \ @@ -93,6 +96,7 @@ CPPSRCS += \ ImageLayerD3D9.cpp \ ColorLayerD3D9.cpp \ CanvasLayerD3D9.cpp \ + DeviceManagerD3D9.cpp \ $(NULL) endif endif diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.cpp b/gfx/layers/d3d9/CanvasLayerD3D9.cpp index 976d3a82eb69..a3ef316b0fd0 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp +++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp @@ -70,9 +70,17 @@ CanvasLayerD3D9::Initialize(const Data& aData) mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height); - device()->CreateTexture(mBounds.width, mBounds.height, 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - getter_AddRefs(mTexture), NULL); + if (mD3DManager->deviceManager()->HasDynamicTextures()) { + device()->CreateTexture(mBounds.width, mBounds.height, 1, D3DUSAGE_DYNAMIC, + D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, + getter_AddRefs(mTexture), NULL); + } else { + // D3DPOOL_MANAGED is fine here since we require Dynamic Textures for D3D9Ex + // devices. + device()->CreateTexture(mBounds.width, mBounds.height, 1, 0, + D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + getter_AddRefs(mTexture), NULL); + } } void @@ -230,7 +238,7 @@ CanvasLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); if (!mGLBufferIsPremultiplied) { device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); diff --git a/gfx/layers/d3d9/ColorLayerD3D9.cpp b/gfx/layers/d3d9/ColorLayerD3D9.cpp index ad7fde80292c..a1f7d12db556 100644 --- a/gfx/layers/d3d9/ColorLayerD3D9.cpp +++ b/gfx/layers/d3d9/ColorLayerD3D9.cpp @@ -76,7 +76,7 @@ ColorLayerD3D9::RenderLayer() device()->SetPixelShaderConstantF(0, color, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::SOLIDCOLORLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::SOLIDCOLORLAYER); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); } diff --git a/gfx/layers/d3d9/ContainerLayerD3D9.cpp b/gfx/layers/d3d9/ContainerLayerD3D9.cpp index f2f3f8fef409..8f1b2a708b45 100644 --- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp +++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp @@ -254,7 +254,7 @@ ContainerLayerD3D9::RenderLayer() opacityVector[0] = opacity; device()->SetPixelShaderConstantF(0, opacityVector, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, renderTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp new file mode 100644 index 000000000000..bf04df6ae4a1 --- /dev/null +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -0,0 +1,562 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "DeviceManagerD3D9.h" +#include "LayerManagerD3D9Shaders.h" +#include "ThebesLayerD3D9.h" +#include "nsIServiceManager.h" +#include "nsIConsoleService.h" +#include "nsPrintfCString.h" + +namespace mozilla { +namespace layers { + +const LPCWSTR kClassName = L"D3D9WindowClass"; + +#define USE_D3D9EX + +typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( + UINT SDKVersion +); + +typedef HRESULT (WINAPI*Direct3DCreate9ExFunc)( + UINT SDKVersion, + IDirect3D9Ex **ppD3D +); + +struct vertex { + float x, y; +}; + +SwapChainD3D9::SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager) + : mDeviceManager(aDeviceManager) + , mWnd(0) +{ + mDeviceManager->mSwapChains.AppendElement(this); +} + +SwapChainD3D9::~SwapChainD3D9() +{ + mDeviceManager->mSwapChains.RemoveElement(this); +} + +bool +SwapChainD3D9::Init(HWND hWnd) +{ + RECT r; + ::GetClientRect(hWnd, &r); + + mWnd = hWnd; + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferFormat = D3DFMT_UNKNOWN; + pp.SwapEffect = D3DSWAPEFFECT_COPY; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + pp.hDeviceWindow = mWnd; + if (r.left == r.right || r.top == r.bottom) { + pp.BackBufferHeight = 1; + pp.BackBufferWidth = 1; + } + + HRESULT hr = mDeviceManager->device()-> + CreateAdditionalSwapChain(&pp, + getter_AddRefs(mSwapChain)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create swap chain for window."); + return false; + } + + return true; +} + +bool +SwapChainD3D9::PrepareForRendering() +{ + RECT r; + if (!::GetClientRect(mWnd, &r)) { + return false; + } + + if (!mDeviceManager->VerifyReadyForRendering()) { + return false; + } + + if (!mSwapChain) { + Init(mWnd); + } + + if (mSwapChain) { + nsRefPtr backBuffer; + mSwapChain->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + getter_AddRefs(backBuffer)); + + D3DSURFACE_DESC desc; + backBuffer->GetDesc(&desc); + + if (desc.Width == r.right - r.left && desc.Height == r.bottom - r.top) { + mDeviceManager->device()->SetRenderTarget(0, backBuffer); + return true; + } + + mSwapChain = nsnull; + + Init(mWnd); + + if (!mSwapChain) { + return false; + } + + mSwapChain->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + getter_AddRefs(backBuffer)); + + mDeviceManager->device()->SetRenderTarget(0, backBuffer); + + return true; + } + return false; +} + +void +SwapChainD3D9::Present(const nsIntRect &aRect) +{ + RECT r; + r.left = aRect.x; + r.top = aRect.y; + r.right = aRect.XMost(); + r.bottom = aRect.YMost(); + + mSwapChain->Present(&r, &r, 0, 0, 0); +} + +void +SwapChainD3D9::Reset() +{ + mSwapChain = nsnull; +} + +#define HAS_CAP(a, b) (((a) & (b)) == (b)) +#define LACKS_CAP(a, b) !(((a) & (b)) == (b)) + +DeviceManagerD3D9::DeviceManagerD3D9() + : mHasDynamicTextures(false) +{ +} + +bool +DeviceManagerD3D9::Init() +{ + WNDCLASSW wc; + HRESULT hr; + + if (!GetClassInfoW(GetModuleHandle(NULL), kClassName, &wc)) { + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = ::DefWindowProc; + wc.lpszClassName = kClassName; + if (!RegisterClassW(&wc)) { + NS_WARNING("Failed to register window class for DeviceManager."); + return false; + } + } + + mFocusWnd = CreateWindow(kClassName, L"D3D9Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, + NULL, GetModuleHandle(NULL), NULL); + + if (!mFocusWnd) { + NS_WARNING("Failed to create DeviceManagerD3D9 Window."); + return false; + } + + HMODULE d3d9 = LoadLibraryW(L"d3d9.dll"); + Direct3DCreate9Func d3d9Create = (Direct3DCreate9Func) + GetProcAddress(d3d9, "Direct3DCreate9"); + Direct3DCreate9ExFunc d3d9CreateEx = (Direct3DCreate9ExFunc) + GetProcAddress(d3d9, "Direct3DCreate9Ex"); + +#ifdef USE_D3D9EX + if (d3d9CreateEx) { + hr = d3d9CreateEx(D3D_SDK_VERSION, getter_AddRefs(mD3D9Ex)); + if (SUCCEEDED(hr)) { + mD3D9 = mD3D9Ex; + } + } +#endif + + if (!mD3D9) { + if (!d3d9Create) { + return false; + } + + mD3D9 = dont_AddRef(d3d9Create(D3D_SDK_VERSION)); + + if (!mD3D9) { + return false; + } + } + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + if (mD3D9Ex) { + hr = mD3D9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + NULL, + getter_AddRefs(mDeviceEx)); + if (SUCCEEDED(hr)) { + mDevice = mDeviceEx; + } + + D3DCAPS9 caps; + if (mDeviceEx->GetDeviceCaps(&caps)) { + if (LACKS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) { + // XXX - Should we actually hit this we'll need a CanvasLayer that + // supports static D3DPOOL_DEFAULT textures. + NS_WARNING("D3D9Ex device not used because of lack of support for \ + dynamic textures. This is unexpected."); + mDevice = nsnull; + mDeviceEx = nsnull; + } + } + } + + if (!mDevice) { + hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + getter_AddRefs(mDevice)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create Device for DeviceManagerD3D9."); + return false; + } + } + + if (!VerifyCaps()) { + return false; + } + + hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS, + getter_AddRefs(mLayerVS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS, + getter_AddRefs(mRGBPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, + getter_AddRefs(mYCbCrPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS, + getter_AddRefs(mSolidColorPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, + D3DUSAGE_WRITEONLY, + 0, + D3DPOOL_DEFAULT, + getter_AddRefs(mVB), + NULL); + + if (FAILED(hr)) { + return false; + } + + vertex *vertices; + hr = mVB->Lock(0, 0, (void**)&vertices, 0); + if (FAILED(hr)) { + return false; + } + + vertices[0].x = vertices[0].y = 0; + vertices[1].x = 1; vertices[1].y = 0; + vertices[2].x = 0; vertices[2].y = 1; + vertices[3].x = 1; vertices[3].y = 1; + + mVB->Unlock(); + + hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); + if (FAILED(hr)) { + return false; + } + + D3DVERTEXELEMENT9 elements[] = { + { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, + D3DDECLUSAGE_POSITION, 0 }, + D3DDECL_END() + }; + + mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD)); + + nsCOMPtr + console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + + D3DADAPTER_IDENTIFIER9 identifier; + mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); + + if (console) { + nsString msg; + msg += + NS_LITERAL_STRING("Direct3D 9 DeviceManager Initialized Succesfully.\nDriver: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)identifier.Driver)); + msg += NS_LITERAL_STRING("\nDescription: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)identifier.Description)); + msg += NS_LITERAL_STRING("\nVersion: "); + msg += NS_ConvertUTF8toUTF16( + nsPrintfCString("%d.%d.%d.%d", + HIWORD(identifier.DriverVersion.HighPart), + LOWORD(identifier.DriverVersion.HighPart), + HIWORD(identifier.DriverVersion.LowPart), + LOWORD(identifier.DriverVersion.LowPart))); + console->LogStringMessage(msg.get()); + } + + return true; +} + +void +DeviceManagerD3D9::SetupRenderState() +{ + mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); + mDevice->SetVertexDeclaration(mVD); + mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); +} + +already_AddRefed +DeviceManagerD3D9::CreateSwapChain(HWND hWnd) +{ + nsRefPtr swapChain = new SwapChainD3D9(this); + + if (!swapChain->Init(hWnd)) { + return nsnull; + } + + return swapChain.forget(); +} + +void +DeviceManagerD3D9::SetShaderMode(ShaderMode aMode) +{ + switch (aMode) { + case RGBLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mRGBPS); + break; + case YCBCRLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mYCbCrPS); + break; + case SOLIDCOLORLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mSolidColorPS); + break; + } +} + +bool +DeviceManagerD3D9::VerifyReadyForRendering() +{ + HRESULT hr = mDevice->TestCooperativeLevel(); + + if (SUCCEEDED(hr)) { + if (IsD3D9Ex()) { + hr = mDeviceEx->CheckDeviceState(mFocusWnd); + if (FAILED(hr)) { + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + hr = mDeviceEx->ResetEx(&pp, NULL); + // Handle D3DERR_DEVICEREMOVED! + if (FAILED(hr)) { + return false; + } + } + } + return true; + } + + if (hr != D3DERR_DEVICENOTRESET) { + return false; + } + + for(unsigned int i = 0; i < mThebesLayers.Length(); i++) { + mThebesLayers[i]->CleanResources(); + } + for(unsigned int i = 0; i < mSwapChains.Length(); i++) { + mSwapChains[i]->Reset(); + } + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + hr = mDevice->Reset(&pp); + + if (FAILED(hr)) { + return false; + } + + return true; +} + +bool +DeviceManagerD3D9::VerifyCaps() +{ + D3DCAPS9 caps; + HRESULT hr = mDevice->GetDeviceCaps(&caps); + + if (FAILED(hr)) { + return false; + } + + if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) { + return false; + } + + if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) { + return false; + } + + if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) || + LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) || + LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) { + return false; + } + + if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) { + return false; + } + + if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) || + HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) || + (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) && + LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) { + return false; + } + + if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) || + LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) { + return false; + } + + if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) { + return false; + } + + if (caps.MaxTextureHeight < 4096 || + caps.MaxTextureWidth < 4096) { + return false; + } + + if ((caps.PixelShaderVersion & 0xffff) < 0x200 || + (caps.VertexShaderVersion & 0xffff) < 0x200) { + return false; + } + + if (HAS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) { + mHasDynamicTextures = true; + } + + return true; +} + +} /* namespace layers */ +} /* namespace mozilla */ diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.h b/gfx/layers/d3d9/DeviceManagerD3D9.h new file mode 100644 index 000000000000..b3b921142447 --- /dev/null +++ b/gfx/layers/d3d9/DeviceManagerD3D9.h @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_DEVICEMANAGERD3D9_H +#define GFX_DEVICEMANAGERD3D9_H + +#include "gfxTypes.h" +#include "nsRect.h" +#include "nsAutoPtr.h" +#include "d3d9.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +class DeviceManagerD3D9; +class ThebesLayerD3D9; + +/** + * SwapChain class, this class manages the swap chain belonging to a + * LayerManagerD3D9. + */ +class THEBES_API SwapChainD3D9 +{ + NS_INLINE_DECL_REFCOUNTING(SwapChainD3D9) +public: + ~SwapChainD3D9(); + + /** + * This function will prepare the device this swap chain belongs to for + * rendering to this swap chain. Only after calling this function can the + * swap chain be drawn to, and only until this function is called on another + * swap chain belonging to this device will the device draw to it. Passed in + * is the size of the swap chain. If the window size differs from the size + * during the last call to this function the swap chain will resize. Note that + * in no case does this function guarantee the backbuffer to still have its + * old content. + */ + bool PrepareForRendering(); + + /** + * This function will present the selected rectangle of the swap chain to + * its associated window. + */ + void Present(const nsIntRect &aRect); + +private: + friend class DeviceManagerD3D9; + + SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager); + + bool Init(HWND hWnd); + + /** + * This causes us to release our swap chain, clearing out our resource usage + * so the master device may reset. + */ + void Reset(); + + nsRefPtr mSwapChain; + nsRefPtr mDeviceManager; + HWND mWnd; +}; + +/** + * Device manager, this class is used by the layer managers to share the D3D9 + * device and create swap chains for the individual windows the layer managers + * belong to. + */ +class THEBES_API DeviceManagerD3D9 +{ +public: + DeviceManagerD3D9(); + + // We want the nsrefcnt return value. So we cannot use the inline refcnt macro + NS_IMPL_ADDREF(DeviceManagerD3D9) + NS_IMPL_RELEASE(DeviceManagerD3D9) + + bool Init(); + + /** + * Sets up the render state for the device for layer rendering. + */ + void SetupRenderState(); + + /** + * Create a swap chain setup to work with the specified window. + */ + already_AddRefed CreateSwapChain(HWND hWnd); + + IDirect3DDevice9 *device() { return mDevice; } + + bool IsD3D9Ex() { return mDeviceEx; } + + bool HasDynamicTextures() { return mHasDynamicTextures; } + + enum ShaderMode { + RGBLAYER, + YCBCRLAYER, + SOLIDCOLORLAYER + }; + + void SetShaderMode(ShaderMode aMode); + + /** + * We keep a list of all thebes layers since we need their D3DPOOL_DEFAULT + * surfaces to be released when we want to reset the device. + */ + nsTArray mThebesLayers; +private: + friend class SwapChainD3D9; + + /** + * This function verifies the device is ready for rendering, internally this + * will test the cooperative level of the device and reset the device if + * needed. If this returns false subsequent rendering calls may return errors. + */ + bool VerifyReadyForRendering(); + + /* Array used to store all swap chains for device resets */ + nsTArray mSwapChains; + + /* The D3D device we use */ + nsRefPtr mDevice; + + /* The D3D9Ex device - only valid on Vista+ with WDDM */ + nsRefPtr mDeviceEx; + + /* An instance of the D3D9 object */ + nsRefPtr mD3D9; + + /* An instance of the D3D9Ex object - only valid on Vista+ with WDDM */ + nsRefPtr mD3D9Ex; + + /* Vertex shader used for layer quads */ + nsRefPtr mLayerVS; + + /* Pixel shader used for RGB textures */ + nsRefPtr mRGBPS; + + /* Pixel shader used for RGB textures */ + nsRefPtr mYCbCrPS; + + /* Pixel shader used for solid colors */ + nsRefPtr mSolidColorPS; + + /* Vertex buffer containing our basic vertex structure */ + nsRefPtr mVB; + + /* Our vertex declaration */ + nsRefPtr mVD; + + /* Our focus window - this is really a dummy window we can associate our + * device with. + */ + HWND mFocusWnd; + + /* If this device supports dynamic textures */ + bool mHasDynamicTextures; + + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD + + /** + * Verifies all required device capabilities are present. + */ + bool VerifyCaps(); +}; + +} /* namespace layers */ +} /* namespace mozilla */ + +#endif /* GFX_DEVICEMANAGERD3D9_H */ diff --git a/gfx/layers/d3d9/ImageLayerD3D9.cpp b/gfx/layers/d3d9/ImageLayerD3D9.cpp index d4fba66d40ed..eedbd1d1847a 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -185,7 +185,7 @@ ImageLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::YCBCRLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER); device()->SetTexture(0, yuvImage->mYTexture); device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); @@ -227,7 +227,7 @@ ImageLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, cairoImage->mTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); @@ -244,6 +244,7 @@ PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9(mozilla::layers::LayerManagerD3D9* aM void PlanarYCbCrImageD3D9::SetData(const PlanarYCbCrImage::Data &aData) { + // XXX - For D3D9Ex we really should just copy to systemmem surfaces here. // For now, we copy the data int width_shift = 0; int height_shift = 0; @@ -309,76 +310,116 @@ PlanarYCbCrImageD3D9::AllocateTextures() { - D3DLOCKED_RECT lockrect; + D3DLOCKED_RECT lockrectY; + D3DLOCKED_RECT lockrectCb; + D3DLOCKED_RECT lockrectCr; PRUint8* src; PRUint8* dest; - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mYTexture), NULL); + nsRefPtr tmpSurfaceY; + nsRefPtr tmpSurfaceCb; + nsRefPtr tmpSurfaceCr; - /* lock the entire texture */ - mYTexture->LockRect(0, &lockrect, NULL, 0); + if (mManager->deviceManager()->IsD3D9Ex()) { + // D3D9Ex does not support the managed pool, could use dynamic textures + // here. But since an Image is immutable static textures are probably a + // better idea. + mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mYTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mCbTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mCrTexture), NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mYSize.width, + mData.mYSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceY), + NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mCbCrSize.width, + mData.mCbCrSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceCb), + NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mCbCrSize.width, + mData.mCbCrSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceCr), + NULL); + tmpSurfaceY->LockRect(&lockrectY, NULL, 0); + tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0); + tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0); + } else { + mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mYTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mCbTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mCrTexture), NULL); + + /* lock the entire texture */ + mYTexture->LockRect(0, &lockrectY, NULL, 0); + mCbTexture->LockRect(0, &lockrectCb, NULL, 0); + mCrTexture->LockRect(0, &lockrectCr, NULL, 0); + } src = mData.mYChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectY.pBits; // copy over data for (int h=0; hUnlockRect(0); - - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCbTexture), NULL); - - - /* lock the entire texture */ - mCbTexture->LockRect(0, &lockrect, NULL, 0); - src = mData.mCbChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectCb.pBits; // copy over data for (int h=0; hUnlockRect(0); - - - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCrTexture), NULL); - - - /* lock the entire texture */ - mCrTexture->LockRect(0, &lockrect, NULL, 0); - src = mData.mCrChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectCr.pBits; // copy over data for (int h=0; hUnlockRect(0); - + if (mManager->deviceManager()->IsD3D9Ex()) { + tmpSurfaceY->UnlockRect(); + tmpSurfaceCb->UnlockRect(); + tmpSurfaceCr->UnlockRect(); + nsRefPtr dstSurface; + mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL); + mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL); + mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL); + } else { + mYTexture->UnlockRect(0); + mCbTexture->UnlockRect(0); + mCrTexture->UnlockRect(0); + } } void @@ -426,31 +467,49 @@ CairoImageD3D9::SetData(const CairoImage::Data &aData) context->SetSource(aData.mSurface); context->Paint(); - //XXX: make sure we're using the correct usage flags - mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, - 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - getter_AddRefs(mTexture), NULL); + if (mManager->deviceManager()->IsD3D9Ex()) { + // D3D9Ex doesn't support managed textures. We could use dynamic textures + // here but since Images are immutable that probably isn't such a great + // idea. + mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, + 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, + getter_AddRefs(mTexture), NULL); + nsRefPtr surface; + mManager->device()->CreateOffscreenPlainSurface(aData.mSize.width, + aData.mSize.height, + D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(surface), + NULL); + D3DLOCKED_RECT lockedRect; + surface->LockRect(&lockedRect, NULL, 0); + for (int y = 0; y < aData.mSize.height; y++) { + memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y, + imageSurface->Data() + imageSurface->Stride() * y, + aData.mSize.width * 4); + } + surface->UnlockRect(); + nsRefPtr dstSurface; + mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(surface, NULL, dstSurface, NULL); + } else { + mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, + 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + getter_AddRefs(mTexture), NULL); + D3DLOCKED_RECT lockrect; + /* lock the entire texture */ + mTexture->LockRect(0, &lockrect, NULL, 0); - D3DLOCKED_RECT lockrect; - /* lock the entire texture */ - mTexture->LockRect(0, &lockrect, NULL, 0); + // copy over data. If we don't need to do any swaping we can + // use memcpy + for (int y = 0; y < aData.mSize.height; y++) { + memcpy((char*)lockrect.pBits + lockrect.Pitch * y, + imageSurface->Data() + imageSurface->Stride() * y, + aData.mSize.width * 4); + } - PRUint8* src = imageSurface->Data(); - //FIX cast - PRUint8* dest = (PRUint8*)lockrect.pBits; - - // copy over data. If we don't need to do any swaping we can - // use memcpy - for (int i=0; iUnlockRect(0); } - - mTexture->UnlockRect(0); } already_AddRefed diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index b29278e6ce14..13a5d56bca63 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -43,25 +43,10 @@ #include "ColorLayerD3D9.h" #include "CanvasLayerD3D9.h" -#include "LayerManagerD3D9Shaders.h" - -#include "nsIServiceManager.h" -#include "nsIConsoleService.h" -#include "nsPrintfCString.h" - namespace mozilla { namespace layers { -struct vertex { - float x, y; -}; - -IDirect3D9 *LayerManagerD3D9::mD3D9 = NULL; - -typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( - UINT SDKVersion -); - +DeviceManagerD3D9 *LayerManagerD3D9::mDeviceManager = nsnull; LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) { @@ -72,145 +57,39 @@ LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) LayerManagerD3D9::~LayerManagerD3D9() { -} + /* Important to release this first since it also holds a reference to the + * device manager + */ + mSwapChain = nsnull; -#define HAS_CAP(a, b) (((a) & (b)) == (b)) -#define LACKS_CAP(a, b) !(((a) & (b)) == (b)) + if (mDeviceManager) { + if (!mDeviceManager->Release()) { + mDeviceManager = nsnull; + } + } +} PRBool LayerManagerD3D9::Initialize() { - if (!mD3D9) { - Direct3DCreate9Func d3d9create = (Direct3DCreate9Func) - GetProcAddress(LoadLibraryW(L"d3d9.dll"), "Direct3DCreate9"); - if (!d3d9create) { - return PR_FALSE; - } + if (!mDeviceManager) { + mDeviceManager = new DeviceManagerD3D9; - mD3D9 = d3d9create(D3D_SDK_VERSION); - if (!mD3D9) { + if (!mDeviceManager->Init()) { + mDeviceManager = nsnull; return PR_FALSE; } } - D3DPRESENT_PARAMETERS pp; - memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + mDeviceManager->AddRef(); - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.SwapEffect = D3DSWAPEFFECT_COPY; - pp.Windowed = TRUE; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - pp.hDeviceWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); + mSwapChain = mDeviceManager-> + CreateSwapChain((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW)); - HRESULT hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - NULL, - D3DCREATE_FPU_PRESERVE | - D3DCREATE_MULTITHREADED | - D3DCREATE_MIXED_VERTEXPROCESSING, - &pp, - getter_AddRefs(mDevice)); - - if (FAILED(hr)) { + if (!mSwapChain) { return PR_FALSE; } - if (!VerifyCaps()) { - return PR_FALSE; - } - - hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS, - getter_AddRefs(mLayerVS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS, - getter_AddRefs(mRGBPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, - getter_AddRefs(mYCbCrPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS, - getter_AddRefs(mSolidColorPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, - 0, - 0, - D3DPOOL_MANAGED, - getter_AddRefs(mVB), - NULL); - - if (FAILED(hr)) { - return PR_FALSE; - } - - vertex *vertices; - hr = mVB->Lock(0, 0, (void**)&vertices, 0); - if (FAILED(hr)) { - return PR_FALSE; - } - - vertices[0].x = vertices[0].y = 0; - vertices[1].x = 1; vertices[1].y = 0; - vertices[2].x = 0; vertices[2].y = 1; - vertices[3].x = 1; vertices[3].y = 1; - - mVB->Unlock(); - - hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); - if (FAILED(hr)) { - return PR_FALSE; - } - - D3DVERTEXELEMENT9 elements[] = { - { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, - D3DDECLUSAGE_POSITION, 0 }, - D3DDECL_END() - }; - - mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD)); - - SetupRenderState(); - - nsCOMPtr - console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); - - D3DADAPTER_IDENTIFIER9 identifier; - mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); - - if (console) { - nsString msg; - msg += - NS_LITERAL_STRING("Direct3D 9 LayerManager Initialized Succesfully.\nDriver: "); - msg += NS_ConvertUTF8toUTF16( - nsDependentCString((const char*)identifier.Driver)); - msg += NS_LITERAL_STRING("\nDescription: "); - msg += NS_ConvertUTF8toUTF16( - nsDependentCString((const char*)identifier.Description)); - msg += NS_LITERAL_STRING("\nVersion: "); - msg += NS_ConvertUTF8toUTF16( - nsPrintfCString("%d.%d.%d.%d", - HIWORD(identifier.DriverVersion.HighPart), - LOWORD(identifier.DriverVersion.HighPart), - HIWORD(identifier.DriverVersion.LowPart), - LOWORD(identifier.DriverVersion.LowPart))); - console->LogStringMessage(msg.get()); - } - return PR_TRUE; } @@ -298,38 +177,21 @@ LayerManagerD3D9::CreateImageContainer() return container.forget(); } -void -LayerManagerD3D9::SetShaderMode(ShaderMode aMode) -{ - switch (aMode) { - case RGBLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mRGBPS); - break; - case YCBCRLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mYCbCrPS); - break; - case SOLIDCOLORLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mSolidColorPS); - break; - } -} - void LayerManagerD3D9::Render() { - if (!SetupBackBuffer()) { + if (!mSwapChain->PrepareForRendering()) { return; } + deviceManager()->SetupRenderState(); + SetupPipeline(); nsIntRect rect; mWidget->GetClientBounds(rect); - mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0xffffffff, 0, 0); + device()->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 0, 0); - mDevice->BeginScene(); + device()->BeginScene(); if (mRootLayer) { const nsIntRect *clipRect = mRootLayer->GetLayer()->GetClipRect(); @@ -344,24 +206,18 @@ LayerManagerD3D9::Render() r.right = rect.width; r.bottom = rect.height; } - mDevice->SetScissorRect(&r); + device()->SetScissorRect(&r); mRootLayer->RenderLayer(); } - mDevice->EndScene(); + device()->EndScene(); if (!mTarget) { const nsIntRect *r; for (nsIntRegionRectIterator iter(mClippingRegion); (r = iter.Next()) != nsnull;) { - RECT rect; - rect.left = r->x; - rect.top = r->y; - rect.right = r->XMost(); - rect.bottom = r->YMost(); - - mDevice->Present(&rect, &rect, NULL, NULL); + mSwapChain->Present(*r); } } else { PaintToTarget(); @@ -387,86 +243,29 @@ LayerManagerD3D9::SetupPipeline() viewMatrix[3][1] = 1.0f; viewMatrix[3][3] = 1.0f; - HRESULT hr = mDevice->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4); + HRESULT hr = device()->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4); if (FAILED(hr)) { NS_WARNING("Failed to set projection shader constant!"); } } -PRBool -LayerManagerD3D9::SetupBackBuffer() -{ - nsRefPtr backBuffer; - mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, - getter_AddRefs(backBuffer)); - - D3DSURFACE_DESC desc; - nsIntRect rect; - mWidget->GetClientBounds(rect); - backBuffer->GetDesc(&desc); - - HRESULT hr = mDevice->TestCooperativeLevel(); - - /* The device is lost or something else is wrong, failure */ - if (FAILED(hr) && hr != D3DERR_DEVICENOTRESET) { - return PR_FALSE; - } - - /* - * If the backbuffer is the right size, and the device is not lost, we can - * safely render without doing anything. - */ - if ((desc.Width == rect.width && desc.Height == rect.height) && - SUCCEEDED(hr)) { - return PR_TRUE; - } - - /* - * Our device is lost or our backbuffer needs resizing, start by clearing - * out all D3DPOOL_DEFAULT surfaces. - */ - for(unsigned int i = 0; i < mThebesLayers.Length(); i++) { - mThebesLayers[i]->CleanResources(); - } - - backBuffer = NULL; - - D3DPRESENT_PARAMETERS pp; - memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); - - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.SwapEffect = D3DSWAPEFFECT_COPY; - pp.Windowed = TRUE; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - pp.hDeviceWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); - - hr = mDevice->Reset(&pp); - if (FAILED(hr)) { - return PR_FALSE; - } - - SetupRenderState(); - - return PR_TRUE; -} - void LayerManagerD3D9::PaintToTarget() { nsRefPtr backBuff; nsRefPtr destSurf; - mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, + device()->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, getter_AddRefs(backBuff)); D3DSURFACE_DESC desc; backBuff->GetDesc(&desc); - mDevice->CreateOffscreenPlainSurface(desc.Width, desc.Height, + device()->CreateOffscreenPlainSurface(desc.Width, desc.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, getter_AddRefs(destSurf), NULL); - mDevice->GetRenderTargetData(backBuff, destSurf); + device()->GetRenderTargetData(backBuff, destSurf); D3DLOCKED_RECT rect; destSurf->LockRect(&rect, NULL, D3DLOCK_READONLY); @@ -483,81 +282,6 @@ LayerManagerD3D9::PaintToTarget() destSurf->UnlockRect(); } -void -LayerManagerD3D9::SetupRenderState() -{ - mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); - mDevice->SetVertexDeclaration(mVD); - mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); - mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); - mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); -} - -PRBool -LayerManagerD3D9::VerifyCaps() -{ - D3DCAPS9 caps; - HRESULT hr = mDevice->GetDeviceCaps(&caps); - - if (FAILED(hr)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) || - LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) || - LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) || - HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) || - (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) && - LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) || - LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) { - return PR_FALSE; - } - - if (caps.MaxTextureHeight < 4096 || - caps.MaxTextureWidth < 4096) { - return PR_FALSE; - } - - if ((caps.PixelShaderVersion & 0xffff) < 0x200 || - (caps.VertexShaderVersion & 0xffff) < 0x200) { - return PR_FALSE; - } - return PR_TRUE; -} - LayerD3D9::LayerD3D9(LayerManagerD3D9 *aManager) : mD3DManager(aManager) { diff --git a/gfx/layers/d3d9/LayerManagerD3D9.h b/gfx/layers/d3d9/LayerManagerD3D9.h index b17c9a4a125b..56f7bcaad45c 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.h +++ b/gfx/layers/d3d9/LayerManagerD3D9.h @@ -46,6 +46,8 @@ #include "gfxContext.h" #include "nsIWidget.h" +#include "DeviceManagerD3D9.h" + namespace mozilla { namespace layers { @@ -122,49 +124,27 @@ public: */ void SetClippingEnabled(PRBool aEnabled); - IDirect3DDevice9 *device() const { return mDevice; } + void SetShaderMode(DeviceManagerD3D9::ShaderMode aMode) + { mDeviceManager->SetShaderMode(aMode); } - enum ShaderMode { - RGBLAYER, - YCBCRLAYER, - SOLIDCOLORLAYER - }; - - void SetShaderMode(ShaderMode aMode); - - nsTArray mThebesLayers; + IDirect3DDevice9 *device() const { return mDeviceManager->device(); } + DeviceManagerD3D9 *deviceManager() const { return mDeviceManager; } private: - /* Direct3D9 instance */ - static IDirect3D9 *mD3D9; + /* Device manager instance */ + static DeviceManagerD3D9 *mDeviceManager; + + /* Swap chain associated with this layer manager */ + nsRefPtr mSwapChain; /* Widget associated with this layer manager */ nsIWidget *mWidget; + /* * Context target, NULL when drawing directly to our swap chain. */ nsRefPtr mTarget; - nsRefPtr mDevice; - - /* Vertex shader used for layer quads */ - nsRefPtr mLayerVS; - - /* Pixel shader used for RGB textures */ - nsRefPtr mRGBPS; - - /* Pixel shader used for RGB textures */ - nsRefPtr mYCbCrPS; - - /* Pixel shader used for solid colors */ - nsRefPtr mSolidColorPS; - - /* Vertex buffer containing our basic vertex structure */ - nsRefPtr mVB; - - /* Our vertex declaration */ - nsRefPtr mVD; - /* Current root layer. */ LayerD3D9 *mRootLayer; @@ -175,32 +155,21 @@ private: * Region we're clipping our current drawing to. */ nsIntRegion mClippingRegion; + /* * Render the current layer tree to the active target. */ void Render(); + /* * Setup the pipeline. */ void SetupPipeline(); - /* - * Setup the backbuffer. - * - * \return PR_TRUE if setup was succesful - */ - PRBool SetupBackBuffer(); - /* - * Setup the render state for the surface. - */ - void SetupRenderState(); + /* * Copies the content of our backbuffer to the set transaction target. */ void PaintToTarget(); - /* - * Verifies all required device capabilities are present. - */ - PRBool VerifyCaps(); }; diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index ac659e431284..786b2fb5aad0 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -39,6 +39,9 @@ #include "gfxPlatform.h" #include "gfxWindowsPlatform.h" +#ifdef CAIRO_HAS_D2D_SURFACE +#include "gfxD2DSurface.h" +#endif namespace mozilla { namespace layers { @@ -69,12 +72,12 @@ ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager) , LayerD3D9(aManager) { mImplData = static_cast(this); - aManager->mThebesLayers.AppendElement(this); + aManager->deviceManager()->mThebesLayers.AppendElement(this); } ThebesLayerD3D9::~ThebesLayerD3D9() { - mD3DManager->mThebesLayers.RemoveElement(this); + mD3DManager->deviceManager()->mThebesLayers.RemoveElement(this); } /** @@ -101,7 +104,8 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) return; } - D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + D3DFORMAT fmt = (UseOpaqueSurface(this) && !mD2DSurface) ? + D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; D3DSURFACE_DESC desc; mTexture->GetLevelDesc(0, &desc); @@ -116,10 +120,8 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) nsIntRect oldBounds = oldVisibleRegion.GetBounds(); nsIntRect newBounds = mVisibleRegion.GetBounds(); - - device()->CreateTexture(newBounds.width, newBounds.height, 1, - D3DUSAGE_RENDERTARGET, fmt, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + + CreateNewTexture(gfxIntSize(newBounds.width, newBounds.height)); // Old visible region will become the region that is covered by both the // old and the new visible region. @@ -191,13 +193,14 @@ ThebesLayerD3D9::RenderLayer() if (mVisibleRegion.IsEmpty()) { return; } - HRESULT hr; nsIntRect visibleRect = mVisibleRegion.GetBounds(); // We differentiate between these formats since D3D9 will only allow us to // call GetDC on an opaque surface. - D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + D3DFORMAT fmt = (UseOpaqueSurface(this) && !mD2DSurface) ? + D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + if (mTexture) { D3DSURFACE_DESC desc; mTexture->GetLevelDesc(0, &desc); @@ -211,103 +214,16 @@ ThebesLayerD3D9::RenderLayer() } if (!mTexture) { - device()->CreateTexture(visibleRect.width, visibleRect.height, 1, - D3DUSAGE_RENDERTARGET, fmt, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + CreateNewTexture(gfxIntSize(visibleRect.width, visibleRect.height)); mValidRegion.SetEmpty(); } if (!mValidRegion.IsEqual(mVisibleRegion)) { nsIntRegion region; region.Sub(mVisibleRegion, mValidRegion); - nsIntRect bounds = region.GetBounds(); - gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; - nsRefPtr destinationSurface; - nsRefPtr context; + DrawRegion(region); - nsRefPtr tmpTexture; - device()->CreateTexture(bounds.width, bounds.height, 1, - 0, fmt, - D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); - - nsRefPtr surf; - HDC dc; - if (UseOpaqueSurface(this)) { - hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); - - if (FAILED(hr)) { - // Uh-oh, bail. - NS_WARNING("Failed to get texture surface level."); - return; - } - - hr = surf->GetDC(&dc); - - if (FAILED(hr)) { - NS_WARNING("Failed to get device context for texture surface."); - return; - } - - destinationSurface = new gfxWindowsSurface(dc); - } else { - // XXX - We may consider retaining a SYSTEMMEM texture texture the size - // of our DEFAULT texture and then use UpdateTexture and add dirty rects - // to update in a single call. - destinationSurface = - gfxPlatform::GetPlatform()-> - CreateOffscreenSurface(gfxIntSize(bounds.width, - bounds.height), - imageFormat); - } - - context = new gfxContext(destinationSurface); - context->Translate(gfxPoint(-bounds.x, -bounds.y)); - LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); - cbInfo.Callback(this, context, region, nsIntRegion(), cbInfo.CallbackData); - - if (UseOpaqueSurface(this)) { - surf->ReleaseDC(dc); - } else { - D3DLOCKED_RECT r; - tmpTexture->LockRect(0, &r, NULL, 0); - - nsRefPtr imgSurface = - new gfxImageSurface((unsigned char *)r.pBits, - gfxIntSize(bounds.width, - bounds.height), - r.Pitch, - imageFormat); - - context = new gfxContext(imgSurface); - context->SetSource(destinationSurface); - context->SetOperator(gfxContext::OPERATOR_SOURCE); - context->Paint(); - - imgSurface = NULL; - - tmpTexture->UnlockRect(0); - } - - nsRefPtr srcSurface; - nsRefPtr dstSurface; - - mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); - - nsIntRegionRectIterator iter(region); - const nsIntRect *iterRect; - while ((iterRect = iter.Next())) { - RECT rect; - rect.left = iterRect->x - bounds.x; - rect.top = iterRect->y - bounds.y; - rect.right = rect.left + iterRect->width; - rect.bottom = rect.top + iterRect->height; - POINT point; - point.x = iterRect->x - visibleRect.x; - point.y = iterRect->y - visibleRect.y; - device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); - } mValidRegion = mVisibleRegion; } @@ -338,7 +254,7 @@ ThebesLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, mTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); @@ -362,5 +278,168 @@ ThebesLayerD3D9::IsEmpty() return !mTexture; } +void +ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion) +{ + HRESULT hr; + nsIntRect visibleRect = mVisibleRegion.GetBounds(); + nsRefPtr context; + +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD2DSurface) { + context = new gfxContext(mD2DSurface); + nsIntRegionRectIterator iter(aRegion); + context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y)); + context->NewPath(); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + context->Rectangle(gfxRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); + } + context->Clip(); + if (mD2DSurface->GetContentType() != gfxASurface::CONTENT_COLOR) { + context->SetOperator(gfxContext::OPERATOR_CLEAR); + context->Paint(); + context->SetOperator(gfxContext::OPERATOR_OVER); + } + LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); + cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + mD2DSurface->Flush(); + + // XXX - This call is quite expensive, we may want to consider doing our + // drawing in a seperate 'validation' iteration. And then flushing once for + // all the D2D surfaces we might have drawn, before doing our D3D9 rendering + // loop. + cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice()); + return; + } +#endif + + D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + nsIntRect bounds = aRegion.GetBounds(); + + gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; + nsRefPtr destinationSurface; + + nsRefPtr tmpTexture; + device()->CreateTexture(bounds.width, bounds.height, 1, + 0, fmt, + D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); + + nsRefPtr surf; + HDC dc; + if (UseOpaqueSurface(this)) { + hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); + + if (FAILED(hr)) { + // Uh-oh, bail. + NS_WARNING("Failed to get texture surface level."); + return; + } + + hr = surf->GetDC(&dc); + + if (FAILED(hr)) { + NS_WARNING("Failed to get device context for texture surface."); + return; + } + + destinationSurface = new gfxWindowsSurface(dc); + } else { + // XXX - We may consider retaining a SYSTEMMEM texture texture the size + // of our DEFAULT texture and then use UpdateTexture and add dirty rects + // to update in a single call. + destinationSurface = + gfxPlatform::GetPlatform()-> + CreateOffscreenSurface(gfxIntSize(bounds.width, + bounds.height), + imageFormat); + } + + context = new gfxContext(destinationSurface); + context->Translate(gfxPoint(-bounds.x, -bounds.y)); + LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); + cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + + if (UseOpaqueSurface(this)) { + surf->ReleaseDC(dc); + } else { + D3DLOCKED_RECT r; + tmpTexture->LockRect(0, &r, NULL, 0); + + nsRefPtr imgSurface = + new gfxImageSurface((unsigned char *)r.pBits, + gfxIntSize(bounds.width, + bounds.height), + r.Pitch, + imageFormat); + + context = new gfxContext(imgSurface); + context->SetSource(destinationSurface); + context->SetOperator(gfxContext::OPERATOR_SOURCE); + context->Paint(); + + imgSurface = NULL; + + tmpTexture->UnlockRect(0); + } + + nsRefPtr srcSurface; + nsRefPtr dstSurface; + + mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + + nsIntRegionRectIterator iter(aRegion); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + RECT rect; + rect.left = iterRect->x - bounds.x; + rect.top = iterRect->y - bounds.y; + rect.right = rect.left + iterRect->width; + rect.bottom = rect.top + iterRect->height; + POINT point; + point.x = iterRect->x - visibleRect.x; + point.y = iterRect->y - visibleRect.y; + device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); + } +} + +void +ThebesLayerD3D9::CreateNewTexture(const gfxIntSize &aSize) +{ + if (aSize.width == 0 | aSize.height == 0) { + // Nothing to do. + return; + } + + mTexture = nsnull; +#ifdef CAIRO_HAS_D2D_SURFACE + if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == + gfxWindowsPlatform::RENDER_DIRECT2D) { + if (mD3DManager->deviceManager()->IsD3D9Ex()) { + // We should have D3D9Ex where we have D2D. + HANDLE sharedHandle = 0; + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), &sharedHandle); + + mD2DSurface = new gfxD2DSurface(sharedHandle, UseOpaqueSurface(this) ? + gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA); + + // If there's an error, go on and do what we always do. + if (mD2DSurface->CairoStatus()) { + mD2DSurface = nsnull; + mTexture = nsnull; + } + } + } +#endif + if (!mTexture) { + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + } +} + } /* namespace layers */ } /* namespace mozilla */ diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.h b/gfx/layers/d3d9/ThebesLayerD3D9.h index 13bb29f3da17..d51a9a86d4c5 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.h +++ b/gfx/layers/d3d9/ThebesLayerD3D9.h @@ -42,7 +42,6 @@ #include "LayerManagerD3D9.h" #include "gfxImageSurface.h" - namespace mozilla { namespace layers { @@ -70,6 +69,15 @@ private: * D3D9 texture */ nsRefPtr mTexture; + + /* This contains the D2D surface if we have one */ + nsRefPtr mD2DSurface; + + /* Have a region of our layer drawn */ + void DrawRegion(const nsIntRegion &aRegion); + + /* Create a new texture */ + void CreateNewTexture(const gfxIntSize &aSize); }; } /* layers */ diff --git a/gfx/thebes/gfxD2DSurface.cpp b/gfx/thebes/gfxD2DSurface.cpp index dc0a92fec3be..dfa1ef1884f5 100644 --- a/gfx/thebes/gfxD2DSurface.cpp +++ b/gfx/thebes/gfxD2DSurface.cpp @@ -36,10 +36,22 @@ #include "gfxD2DSurface.h" #include "cairo.h" #include "cairo-win32.h" +#include "gfxWindowsPlatform.h" gfxD2DSurface::gfxD2DSurface(HWND aWnd, gfxContentType aContent) { - Init(cairo_d2d_surface_create_for_hwnd(aWnd, (cairo_content_t)aContent)); + Init(cairo_d2d_surface_create_for_hwnd( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + aWnd, + (cairo_content_t)aContent)); +} + +gfxD2DSurface::gfxD2DSurface(HANDLE handle, gfxContentType aContent) +{ + Init(cairo_d2d_surface_create_for_handle( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + handle, + (cairo_content_t)aContent)); } gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) @@ -50,7 +62,10 @@ gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) gfxD2DSurface::gfxD2DSurface(const gfxIntSize& size, gfxImageFormat imageFormat) { - Init(cairo_d2d_surface_create((cairo_format_t)imageFormat, size.width, size.height)); + Init(cairo_d2d_surface_create( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + (cairo_format_t)imageFormat, + size.width, size.height)); } gfxD2DSurface::~gfxD2DSurface() diff --git a/gfx/thebes/gfxD2DSurface.h b/gfx/thebes/gfxD2DSurface.h index 6d3748cc705f..1a3d641fdc57 100644 --- a/gfx/thebes/gfxD2DSurface.h +++ b/gfx/thebes/gfxD2DSurface.h @@ -51,6 +51,7 @@ public: gfxD2DSurface(const gfxIntSize& size, gfxImageFormat imageFormat = ImageFormatRGB24); + gfxD2DSurface(HANDLE handle, gfxContentType aContent); gfxD2DSurface(cairo_surface_t *csurf); diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index c3b1b6249189..ae50a3969917 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -170,6 +170,7 @@ gfxWindowsPlatform::gfxWindowsPlatform() #ifdef CAIRO_HAS_D2D_SURFACE NS_RegisterMemoryReporter(new D2DCacheReporter()); + mD2DDevice = NULL; #endif #ifdef CAIRO_HAS_DWRITE_FONT nsresult rv; @@ -213,7 +214,8 @@ gfxWindowsPlatform::gfxWindowsPlatform() #ifndef CAIRO_HAS_D2D_SURFACE return; #else - if (!cairo_d2d_has_support()) { + mD2DDevice = cairo_d2d_create_device(); + if (!mD2DDevice) { return; } #ifdef CAIRO_HAS_DWRITE_FONT @@ -235,6 +237,11 @@ gfxWindowsPlatform::~gfxWindowsPlatform() { // not calling FT_Done_FreeType because cairo may still hold references to // these FT_Faces. See bug 458169. +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD2DDevice) { + cairo_release_device(mD2DDevice); + } +#endif } gfxPlatformFontList* diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 10d1756afb73..9b8d6230de39 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -214,6 +214,9 @@ public: #ifdef CAIRO_HAS_DWRITE_FONT IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } #endif +#ifdef CAIRO_HAS_D2D_SURFACE + cairo_device_t *GetD2DDevice() { return mD2DDevice; } +#endif #ifdef MOZ_FT2_FONTS FT_Library GetFTLibrary(); @@ -233,6 +236,9 @@ private: #ifdef CAIRO_HAS_DWRITE_FONT nsRefPtr mDWriteFactory; #endif +#ifdef CAIRO_HAS_D2D_SURFACE + cairo_device_t *mD2DDevice; +#endif virtual qcms_profile* GetPlatformCMSOutputProfile(); diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index e2c747c078c3..363064660f31 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -131,12 +131,14 @@ void GeckoChildProcessHost::InitWindowsGroupID() #endif bool -GeckoChildProcessHost::SyncLaunch(std::vector aExtraOpts) +GeckoChildProcessHost::SyncLaunch(std::vector aExtraOpts, int aTimeoutMs) { #ifdef XP_WIN InitWindowsGroupID(); #endif + PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? + PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT; MessageLoop* ioLoop = XRE_GetIOMessageLoop(); NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI"); @@ -144,15 +146,29 @@ GeckoChildProcessHost::SyncLaunch(std::vector aExtraOpts) NewRunnableMethod(this, &GeckoChildProcessHost::PerformAsyncLaunch, aExtraOpts)); - // NB: this uses a different mechanism than the chromium parent // class. MonitorAutoEnter mon(mMonitor); + PRIntervalTime waitStart = PR_IntervalNow(); + PRIntervalTime current; + + // We'll receive several notifications, we need to exit when we + // have either successfully launched or have timed out. while (!mLaunched) { - mon.Wait(); + mon.Wait(timeoutTicks); + + if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) { + current = PR_IntervalNow(); + PRIntervalTime elapsed = current - waitStart; + if (elapsed > timeoutTicks) { + break; + } + timeoutTicks = timeoutTicks - elapsed; + waitStart = current; + } } - return true; + return mLaunched; } bool diff --git a/ipc/glue/GeckoChildProcessHost.h b/ipc/glue/GeckoChildProcessHost.h index 6a795db1d8bc..7f6725c8149e 100644 --- a/ipc/glue/GeckoChildProcessHost.h +++ b/ipc/glue/GeckoChildProcessHost.h @@ -64,7 +64,7 @@ public: ~GeckoChildProcessHost(); - bool SyncLaunch(std::vector aExtraOpts=std::vector()); + bool SyncLaunch(std::vector aExtraOpts=std::vector(), int32 timeoutMs=0); bool AsyncLaunch(std::vector aExtraOpts=std::vector()); bool PerformAsyncLaunch(std::vector aExtraOpts=std::vector()); diff --git a/ipc/ipdl/test/cxx/Makefile.in b/ipc/ipdl/test/cxx/Makefile.in index b33faeff698a..4f7a6c52aa0c 100644 --- a/ipc/ipdl/test/cxx/Makefile.in +++ b/ipc/ipdl/test/cxx/Makefile.in @@ -78,6 +78,7 @@ IPDLTESTS = \ TestShutdown \ TestStackHooks \ TestSyncWakeup \ + TestSyncHang \ $(NULL) ifeq ($(OS_ARCH),Linux) diff --git a/ipc/ipdl/test/cxx/PTestSyncHang.ipdl b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl new file mode 100644 index 000000000000..861825d5c9ba --- /dev/null +++ b/ipc/ipdl/test/cxx/PTestSyncHang.ipdl @@ -0,0 +1,15 @@ + +namespace mozilla { +namespace _ipdltest { + + +protocol PTestSyncHang { + +child: + __delete__(); + +}; + + +} // namespace mozilla +} // namespace _ipdltest diff --git a/ipc/ipdl/test/cxx/TestSyncHang.cpp b/ipc/ipdl/test/cxx/TestSyncHang.cpp new file mode 100644 index 000000000000..2035ac5320fb --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncHang.cpp @@ -0,0 +1,71 @@ +#include "TestSyncHang.h" +#include "mozilla/ipc/GeckoChildProcessHost.h" + +#include "IPDLUnitTests.h" // fail etc. + +using std::vector; +using std::string; + +namespace mozilla { +namespace _ipdltest { + +//----------------------------------------------------------------------------- +// parent + +mozilla::ipc::GeckoChildProcessHost* gSyncHangSubprocess; + +TestSyncHangParent::TestSyncHangParent() +{ + MOZ_COUNT_CTOR(TestSyncHangParent); +} + +TestSyncHangParent::~TestSyncHangParent() +{ + MOZ_COUNT_DTOR(TestSyncHangParent); +} + +void +DeleteSyncHangSubprocess(MessageLoop* uiLoop) +{ + delete gSyncHangSubprocess; +} + +void +DeferredSyncHangParentShutdown() +{ + // ping to DeleteSubprocess + XRE_GetIOMessageLoop()->PostTask( + FROM_HERE, + NewRunnableFunction(DeleteSyncHangSubprocess, MessageLoop::current())); +} + +void +TestSyncHangParent::Main() +{ + vector args; + args.push_back("fake/path"); + gSyncHangSubprocess = new mozilla::ipc::GeckoChildProcessHost(GeckoProcessType_Plugin); + bool launched = gSyncHangSubprocess->SyncLaunch(args, 2); + if (launched) + fail("Calling SyncLaunch with an invalid path should return false"); + + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableFunction(DeferredSyncHangParentShutdown)); + Close(); +} + +//----------------------------------------------------------------------------- +// child + +TestSyncHangChild::TestSyncHangChild() +{ + MOZ_COUNT_CTOR(TestSyncHangChild); +} + +TestSyncHangChild::~TestSyncHangChild() +{ + MOZ_COUNT_DTOR(TestSyncHangChild); +} + +} // namespace _ipdltest +} // namespace mozilla diff --git a/ipc/ipdl/test/cxx/TestSyncHang.h b/ipc/ipdl/test/cxx/TestSyncHang.h new file mode 100644 index 000000000000..314e1733eae2 --- /dev/null +++ b/ipc/ipdl/test/cxx/TestSyncHang.h @@ -0,0 +1,56 @@ +#ifndef mozilla__ipdltest_TestSyncHang_h +#define mozilla__ipdltest_TestSyncHang_h 1 + +#include "mozilla/_ipdltest/IPDLUnitTests.h" + +#include "mozilla/_ipdltest/PTestSyncHangParent.h" +#include "mozilla/_ipdltest/PTestSyncHangChild.h" + +namespace mozilla { +namespace _ipdltest { + + +class TestSyncHangParent : + public PTestSyncHangParent +{ +public: + TestSyncHangParent(); + virtual ~TestSyncHangParent(); + + void Main(); + +protected: + NS_OVERRIDE + virtual void ActorDestroy(ActorDestroyReason why) + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + passed("ok"); + QuitParent(); + } +}; + + +class TestSyncHangChild : + public PTestSyncHangChild +{ +public: + TestSyncHangChild(); + virtual ~TestSyncHangChild(); + +protected: + NS_OVERRIDE + virtual void ActorDestroy(ActorDestroyReason why) + { + if (NormalShutdown != why) + fail("unexpected destruction!"); + QuitChild(); + } +}; + + +} // namespace _ipdltest +} // namespace mozilla + + +#endif // ifndef mozilla__ipdltest_TestSyncHang_h diff --git a/ipc/ipdl/test/cxx/ipdl.mk b/ipc/ipdl/test/cxx/ipdl.mk index fe34b0201289..f9968f8a51d0 100644 --- a/ipc/ipdl/test/cxx/ipdl.mk +++ b/ipc/ipdl/test/cxx/ipdl.mk @@ -30,5 +30,6 @@ IPDLSRCS = \ PTestShutdownSubsub.ipdl \ PTestStackHooks.ipdl \ PTestSyncWakeup.ipdl \ + PTestSyncHang.ipdl \ PTestSysVShmem.ipdl \ $(NULL) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 31edd94bb074..d9970839bea1 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -7611,13 +7611,14 @@ UpdateViewsForTree(nsIFrame* aFrame, nsIViewManager* aViewManager, if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) || (child->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { // only do frames that don't have placeholders - if (nsGkAtoms::placeholderFrame == child->GetType()) { // placeholder - // get out of flow frame and start over there + if (nsGkAtoms::placeholderFrame == child->GetType()) { + // do the out-of-flow frame and its continuations nsIFrame* outOfFlowFrame = nsPlaceholderFrame::GetRealFrameForPlaceholder(child); - - DoApplyRenderingChangeToTree(outOfFlowFrame, aViewManager, - aFrameManager, aChange); + do { + DoApplyRenderingChangeToTree(outOfFlowFrame, aViewManager, + aFrameManager, aChange); + } while (outOfFlowFrame = outOfFlowFrame->GetNextContinuation()); } else if (childList == nsGkAtoms::popupList) { DoApplyRenderingChangeToTree(child, aViewManager, aFrameManager, aChange); diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 6a10e75d54d8..e7aa916fd075 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -3328,6 +3328,8 @@ DocumentViewerImpl::GetPopupNode(nsIDOMNode** aNode) { NS_ENSURE_ARG_POINTER(aNode); + *aNode = nsnull; + // get the document nsIDocument* document = GetDocument(); NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); @@ -3340,7 +3342,22 @@ DocumentViewerImpl::GetPopupNode(nsIDOMNode** aNode) NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); // get the popup node - NS_IF_ADDREF(*aNode = root->GetPopupNode()); + nsCOMPtr node = root->GetPopupNode(); +#ifdef MOZ_XUL + if (!node) { + nsPIDOMWindow* rootWindow = root->GetWindow(); + if (rootWindow) { + nsCOMPtr rootDoc = do_QueryInterface(rootWindow->GetExtantDocument()); + if (rootDoc) { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + node = pm->GetLastTriggerPopupNode(rootDoc); + } + } + } + } +#endif + node.swap(*aNode); } return NS_OK; diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 737b58072fd9..0801ccd3237e 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -22,6 +22,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Mats Palmgren * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -771,12 +772,13 @@ nsresult nsFrameManager::ReparentStyleContext(nsIFrame* aFrame) { if (nsGkAtoms::placeholderFrame == aFrame->GetType()) { - // Also reparent the out-of-flow + // Also reparent the out-of-flow and all its continuations. nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(aFrame); NS_ASSERTION(outOfFlow, "no out-of-flow frame"); - - ReparentStyleContext(outOfFlow); + do { + ReparentStyleContext(outOfFlow); + } while (outOfFlow = outOfFlow->GetNextContinuation()); } // DO NOT verify the style tree before reparenting. The frame @@ -1470,13 +1472,15 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, // |nsFrame::GetParentStyleContextFrame| checks being out // of flow so that this works correctly. - ReResolveStyleContext(aPresContext, outOfFlowFrame, - content, aChangeList, - NS_SubtractHint(aMinChange, - nsChangeHint_ReflowFrame), - childRestyleHint, - fireAccessibilityEvents, - aRestyleTracker); + do { + ReResolveStyleContext(aPresContext, outOfFlowFrame, + content, aChangeList, + NS_SubtractHint(aMinChange, + nsChangeHint_ReflowFrame), + childRestyleHint, + fireAccessibilityEvents, + aRestyleTracker); + } while (outOfFlowFrame = outOfFlowFrame->GetNextContinuation()); // reresolve placeholder's context under the same parent // as the out-of-flow frame diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 64b25b7ec2fd..ed2ce8c01a73 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1259,13 +1259,17 @@ pref("editor.positioning.offset", 0); pref("dom.max_chrome_script_run_time", 20); pref("dom.max_script_run_time", 10); +#ifndef DEBUG // How long a plugin is allowed to process a synchronous IPC message // before we consider it "hung". -#ifndef DEBUG pref("dom.ipc.plugins.timeoutSecs", 45); +// How long a plugin launch is allowed to take before +// we consider it failed. +pref("dom.ipc.plugins.processLaunchTimeoutSecs", 45); #else // No timeout in DEBUG builds pref("dom.ipc.plugins.timeoutSecs", 0); +pref("dom.ipc.plugins.processLaunchTimeoutSecs", 0); #endif #ifndef ANDROID diff --git a/profile/dirserviceprovider/src/nsProfileLock.cpp b/profile/dirserviceprovider/src/nsProfileLock.cpp index 4eeb25656e9e..4d983de7dd87 100644 --- a/profile/dirserviceprovider/src/nsProfileLock.cpp +++ b/profile/dirserviceprovider/src/nsProfileLock.cpp @@ -142,12 +142,12 @@ static int setupPidLockCleanup; PRCList nsProfileLock::mPidLockList = PR_INIT_STATIC_CLIST(&nsProfileLock::mPidLockList); -void nsProfileLock::RemovePidLockFiles() +void nsProfileLock::RemovePidLockFiles(PRBool aFatalSignal) { while (!PR_CLIST_IS_EMPTY(&mPidLockList)) { nsProfileLock *lock = static_cast(mPidLockList.next); - lock->Unlock(); + lock->Unlock(aFatalSignal); } } @@ -163,7 +163,7 @@ void nsProfileLock::FatalSignalHandler(int signo, siginfo_t *info, void *context) { // Remove any locks still held. - RemovePidLockFiles(); + RemovePidLockFiles(PR_TRUE); // Chain to the old handler, which may exit. struct sigaction *oldact = nsnull; @@ -385,7 +385,7 @@ nsresult nsProfileLock::LockWithSymlink(const nsACString& lockFilePath, PRBool a if (!setupPidLockCleanup++) { // Clean up on normal termination. - atexit(RemovePidLockFiles); + atexit(RemovePidLockFilesExiting); // Clean up on abnormal termination, using POSIX sigaction. // Don't arm a handler if the signal is being ignored, e.g., @@ -652,7 +652,7 @@ nsresult nsProfileLock::Lock(nsILocalFile* aProfileDir, } -nsresult nsProfileLock::Unlock() +nsresult nsProfileLock::Unlock(PRBool aFatalSignal) { nsresult rv = NS_OK; @@ -675,7 +675,14 @@ nsresult nsProfileLock::Unlock() { PR_REMOVE_LINK(this); (void) unlink(mPidLockFileName); - free(mPidLockFileName); + + // Only free mPidLockFileName if we're not in the fatal signal + // handler. The problem is that a call to free() might be the + // cause of this fatal signal. If so, calling free() might cause + // us to wait on the malloc implementation's lock. We're already + // holding this lock, so we'll deadlock. See bug 522332. + if (!aFatalSignal) + free(mPidLockFileName); mPidLockFileName = nsnull; } else if (mLockFileDesc != -1) diff --git a/profile/dirserviceprovider/src/nsProfileLock.h b/profile/dirserviceprovider/src/nsProfileLock.h index 78471d1aaef4..f52c4c39c248 100644 --- a/profile/dirserviceprovider/src/nsProfileLock.h +++ b/profile/dirserviceprovider/src/nsProfileLock.h @@ -82,7 +82,13 @@ public: * @throws NS_ERROR_FILE_ACCESS_DENIED if the profile is locked. */ nsresult Lock(nsILocalFile* aProfileDir, nsIProfileUnlocker* *aUnlocker); - nsresult Unlock(); + + /** + * Unlock a profile directory. If you're unlocking the directory because + * the application is in the process of shutting down because of a fatal + * signal, set aFatalSignal to PR_TRUE. + */ + nsresult Unlock(PRBool aFatalSignal = PR_FALSE); private: PRPackedBool mHaveLock; @@ -92,7 +98,17 @@ private: #elif defined (XP_OS2) LHANDLE mLockFileHandle; #elif defined (XP_UNIX) - static void RemovePidLockFiles(); + + static void RemovePidLockFilesExiting() + { + // We can't implement this function with a default parameter on + // RemovePidLockFiles(aFatalSignal) since we register + // atexit(RemovePidLockFilesExiting). + + RemovePidLockFiles(PR_FALSE); + } + + static void RemovePidLockFiles(PRBool aFatalSignal); static void FatalSignalHandler(int signo, siginfo_t *info, void *context); static PRCList mPidLockList; diff --git a/toolkit/components/console/hudservice/HUDService.jsm b/toolkit/components/console/hudservice/HUDService.jsm index 1c05ab80aa04..425642fd7934 100644 --- a/toolkit/components/console/hudservice/HUDService.jsm +++ b/toolkit/components/console/hudservice/HUDService.jsm @@ -2485,8 +2485,10 @@ JSTerm.prototype = { this.createSandbox(); this.inputNode = this.mixins.inputNode; this.scrollToNode = this.mixins.scrollToNode; - let eventHandler = this.keyDown(); - this.inputNode.addEventListener('keypress', eventHandler, false); + let eventHandlerKeyDown = this.keyDown(); + this.inputNode.addEventListener('keypress', eventHandlerKeyDown, false); + let eventHandlerInput = this.inputEventHandler(); + this.inputNode.addEventListener('input', eventHandlerInput, false); this.outputNode = this.mixins.outputNode; if (this.mixins.cssClassOverride) { this.cssClassOverride = this.mixins.cssClassOverride; @@ -2614,6 +2616,16 @@ JSTerm.prototype = { outputNode.lastTimestamp = 0; }, + inputEventHandler: function JSTF_inputEventHandler() + { + var self = this; + function handleInputEvent(aEvent) { + self.inputNode.setAttribute("rows", + Math.min(8, self.inputNode.value.split("\n").length)); + } + return handleInputEvent; + }, + keyDown: function JSTF_keyDown(aEvent) { var self = this; @@ -2657,6 +2669,7 @@ JSTerm.prototype = { case 13: // return self.execute(); + aEvent.preventDefault(); break; case 38: // up arrow: history previous @@ -2771,7 +2784,7 @@ JSTerm.prototype = { { var firstLineBreak = this.codeInputString.indexOf("\n"); return ((firstLineBreak == -1) || - (this.codeInputString.selectionStart <= firstLineBreak)); + (this.inputNode.selectionStart <= firstLineBreak)); }, caretInLastLine: function JSTF_caretInLastLine() @@ -2951,6 +2964,8 @@ JSTermFirefoxMixin.prototype = { { let inputNode = this.xulElementFactory("textbox"); inputNode.setAttribute("class", "jsterm-input-node"); + inputNode.setAttribute("multiline", "true"); + inputNode.setAttribute("rows", "1"); if (this.existingConsoleNode == undefined) { // create elements diff --git a/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js b/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js index b68aa940d280..de91282372cd 100644 --- a/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js +++ b/toolkit/components/console/hudservice/tests/browser/browser_HUDServiceTestsAll.js @@ -573,6 +573,39 @@ function testCompletion() is(input.selectionEnd, 23, "end selection is alright"); } +function testJSInputExpand() +{ + let HUD = HUDService.hudWeakReferences[hudId].get(); + let jsterm = HUD.jsterm; + let input = jsterm.inputNode; + input.focus(); + + is(input.getAttribute("multiline"), "true", "multiline is enabled"); + + // Tests if the inputNode expands. + input.value = "hello\nworld\n"; + let length = input.value.length; + input.selectionEnd = length; + input.selectionStart = length; + // Performs an "d". This will trigger/test for the input event that should + // change the "row" attribute of the inputNode. + EventUtils.synthesizeKey("d", {}); + is(input.getAttribute("rows"), "3", "got 3 rows"); + + // Add some more rows. Tests for the 8 row limit. + input.value = "row1\nrow2\nrow3\nrow4\nrow5\nrow6\nrow7\nrow8\nrow9\nrow10\n"; + length = input.value.length; + input.selectionEnd = length; + input.selectionStart = length; + EventUtils.synthesizeKey("d", {}); + is(input.getAttribute("rows"), "8", "got 8 rows"); + + // Test if the inputNode shrinks again. + input.value = ""; + EventUtils.synthesizeKey("d", {}); + is(input.getAttribute("rows"), "1", "got 1 row"); +} + function testExecutionScope() { content.location.href = TEST_URI; @@ -844,6 +877,7 @@ function test() { testExecutionScope(); testCompletion(); testPropertyProvider(); + testJSInputExpand(); testNet(); }); }, false); diff --git a/toolkit/themes/pinstripe/global/headsUpDisplay.css b/toolkit/themes/pinstripe/global/headsUpDisplay.css index 640812ef786e..5e03428f35cd 100644 --- a/toolkit/themes/pinstripe/global/headsUpDisplay.css +++ b/toolkit/themes/pinstripe/global/headsUpDisplay.css @@ -157,6 +157,10 @@ -moz-appearance: none !important; } +.jsterm-input-node textarea { + overflow-x: hidden; +} + .jsterm-output-line { font-size: 1.2em; } diff --git a/toolkit/themes/winstripe/global/headsUpDisplay.css b/toolkit/themes/winstripe/global/headsUpDisplay.css index f4b38950ecf2..e1d1af92c934 100644 --- a/toolkit/themes/winstripe/global/headsUpDisplay.css +++ b/toolkit/themes/winstripe/global/headsUpDisplay.css @@ -156,6 +156,10 @@ -moz-appearance: none !important; } +.jsterm-input-node textarea { + overflow-x: hidden; +} + .jsterm-output-line { font-size: 1.2em; } diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp index 8c4afa6e8c05..4969e0344a0c 100644 --- a/xpcom/base/nsSystemInfo.cpp +++ b/xpcom/base/nsSystemInfo.cpp @@ -133,7 +133,10 @@ nsSystemInfo::SetInt32Property(const nsAString &aPropertyName, { NS_WARN_IF_FALSE(aValue > 0, "Unable to read system value"); if (aValue > 0) { - nsresult rv = SetPropertyAsInt32(aPropertyName, aValue); +#ifdef DEBUG + nsresult rv = +#endif + SetPropertyAsInt32(aPropertyName, aValue); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to set property"); } } @@ -144,7 +147,10 @@ nsSystemInfo::SetUint64Property(const nsAString &aPropertyName, { NS_WARN_IF_FALSE(aValue > 0, "Unable to read system value"); if (aValue > 0) { - nsresult rv = SetPropertyAsUint64(aPropertyName, aValue); +#ifdef DEBUG + nsresult rv = +#endif + SetPropertyAsUint64(aPropertyName, aValue); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Unable to set property"); } } diff --git a/xpcom/tests/TestArray.cpp b/xpcom/tests/TestArray.cpp index 6db951976879..216e73fce5dc 100644 --- a/xpcom/tests/TestArray.cpp +++ b/xpcom/tests/TestArray.cpp @@ -113,7 +113,10 @@ const char* AssertEqual(PRInt32 aValue1, PRInt32 aValue2) void DumpArray(nsISupportsArray* aArray, PRInt32 aExpectedCount, PRInt32 aElementIDs[], PRInt32 aExpectedTotal) { PRUint32 cnt = 0; - nsresult rv = aArray->Count(&cnt); +#ifdef DEBUG + nsresult rv = +#endif + aArray->Count(&cnt); NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); PRInt32 count = cnt; PRInt32 index; diff --git a/xpcom/tests/TestCRT.cpp b/xpcom/tests/TestCRT.cpp index b701af20664b..f33dea2f77bc 100644 --- a/xpcom/tests/TestCRT.cpp +++ b/xpcom/tests/TestCRT.cpp @@ -61,8 +61,15 @@ PRIntn sign(PRIntn val) { // iso-latin-1 strings, so the comparison must be valid. static void Check(const char* s1, const char* s2, PRIntn n) { - PRIntn clib = PL_strcmp(s1, s2); - PRIntn clib_n = PL_strncmp(s1, s2, n); +#ifdef DEBUG + PRIntn clib = +#endif + PL_strcmp(s1, s2); + +#ifdef DEBUG + PRIntn clib_n = +#endif + PL_strncmp(s1, s2, n); nsAutoString t1,t2; t1.AssignWithConversion(s1); @@ -70,8 +77,15 @@ static void Check(const char* s1, const char* s2, PRIntn n) const PRUnichar* us1 = t1.get(); const PRUnichar* us2 = t2.get(); - PRIntn u2 = nsCRT::strcmp(us1, us2); - PRIntn u2_n = nsCRT::strncmp(us1, us2, n); +#ifdef DEBUG + PRIntn u2 = +#endif + nsCRT::strcmp(us1, us2); + +#ifdef DEBUG + PRIntn u2_n = +#endif + nsCRT::strncmp(us1, us2, n); NS_ASSERTION(sign(clib) == sign(u2), "strcmp"); NS_ASSERTION(sign(clib_n) == sign(u2_n), "strncmp"); diff --git a/xpcom/tests/TestPipes.cpp b/xpcom/tests/TestPipes.cpp index 20cb10bc2738..47c267b05fdc 100644 --- a/xpcom/tests/TestPipes.cpp +++ b/xpcom/tests/TestPipes.cpp @@ -327,7 +327,11 @@ TestShortWrites(nsIInputStream* in, nsIOutputStream* out) //printf("calling Flush\n"); out->Flush(); //printf("calling WaitForReceipt\n"); - const PRUint32 received = receiver->WaitForReceipt(writeCount); + +#ifdef DEBUG + const PRUint32 received = +#endif + receiver->WaitForReceipt(writeCount); NS_ASSERTION(received == writeCount, "received wrong amount"); } rv = out->Close();