diff --git a/b2g/app/Makefile.in b/b2g/app/Makefile.in index e487cca3a6..f667a77fc7 100644 --- a/b2g/app/Makefile.in +++ b/b2g/app/Makefile.in @@ -10,11 +10,7 @@ NSDISTMODE = copy include $(topsrcdir)/config/rules.mk -APP_ICON = b2g - -ifeq ($(OS_ARCH),WINNT) -REDIT_PATH = $(LIBXUL_DIST)/bin -endif +APP_ICON = app APP_BINARY = $(MOZ_APP_NAME)$(BIN_SUFFIX) @@ -54,7 +50,7 @@ tools repackage:: $(libs-preqs) rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ $(DIST)/$(APP_NAME).app/Contents/MacOS $(RM) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(PROGRAM) rsync -aL $(PROGRAM) $(DIST)/$(APP_NAME).app/Contents/MacOS - cp -RL $(srcdir)/b2g.icns $(DIST)/$(APP_NAME).app/Contents/Resources/$(MOZ_APP_NAME).icns + cp -RL $(DIST)/branding/app.icns $(DIST)/$(APP_NAME).app/Contents/Resources/$(MOZ_APP_NAME).icns printf APPLMOZB > $(DIST)/$(APP_NAME).app/Contents/PkgInfo else # MOZ_WIDGET_TOOLKIT != cocoa @@ -64,11 +60,11 @@ libs:: # Copy the app icon for b2g-desktop ifeq ($(OS_ARCH),WINNT) - cp $(srcdir)/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/$(APP_ICON).ico - $(REDIT_PATH)/redit$(HOST_BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY) $(srcdir)/$(APP_ICON).ico - cp $(srcdir)/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/default.ico + cp $(DIST)/branding/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/$(APP_ICON).ico + $(DIST)/bin/redit$(HOST_BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY) $(DIST)/branding/$(APP_ICON).ico + cp $(DIST)/branding/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/default.ico else ifneq (gonk,$(MOZ_WIDGET_TOOLKIT)) - cp $(srcdir)/default.png $(DIST)/bin/chrome/icons/default/default.png + cp $(DIST)/branding/default.png $(DIST)/bin/chrome/icons/default/default.png endif endif diff --git a/b2g/branding/official/Makefile.in b/b2g/branding/official/Makefile.in new file mode 100644 index 0000000000..2188ac07f5 --- /dev/null +++ b/b2g/branding/official/Makefile.in @@ -0,0 +1,27 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include $(topsrcdir)/config/config.mk + +ifeq ($(MOZ_WIDGET_TOOLKIT),windows) +BRANDING_FILES := \ + app.ico \ + $(NULL) +endif + +ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) +BRANDING_FILES := \ + app.icns \ + $(NULL) +endif + +ifdef MOZ_WIDGET_GTK +BRANDING_FILES := \ + default.png \ + $(NULL) +endif + +BRANDING_DEST := $(DIST)/branding +BRANDING_TARGET := export +INSTALL_TARGETS += BRANDING diff --git a/b2g/branding/official/configure.sh b/b2g/branding/official/configure.sh index 00e73ee0fa..127a0f1a17 100755 --- a/b2g/branding/official/configure.sh +++ b/b2g/branding/official/configure.sh @@ -3,5 +3,4 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. MOZ_APP_DISPLAYNAME=B2G -ANDROID_PACKAGE_NAME=org.mozilla.b2g MOZ_UPDATER= diff --git a/b2g/branding/unofficial/Makefile.in b/b2g/branding/unofficial/Makefile.in new file mode 100644 index 0000000000..2188ac07f5 --- /dev/null +++ b/b2g/branding/unofficial/Makefile.in @@ -0,0 +1,27 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +include $(topsrcdir)/config/config.mk + +ifeq ($(MOZ_WIDGET_TOOLKIT),windows) +BRANDING_FILES := \ + app.ico \ + $(NULL) +endif + +ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) +BRANDING_FILES := \ + app.icns \ + $(NULL) +endif + +ifdef MOZ_WIDGET_GTK +BRANDING_FILES := \ + default.png \ + $(NULL) +endif + +BRANDING_DEST := $(DIST)/branding +BRANDING_TARGET := export +INSTALL_TARGETS += BRANDING diff --git a/b2g/branding/unofficial/app.icns b/b2g/branding/unofficial/app.icns new file mode 100644 index 0000000000..eba850aaee Binary files /dev/null and b/b2g/branding/unofficial/app.icns differ diff --git a/b2g/branding/unofficial/app.ico b/b2g/branding/unofficial/app.ico new file mode 100644 index 0000000000..5d4a61dc92 Binary files /dev/null and b/b2g/branding/unofficial/app.ico differ diff --git a/b2g/branding/unofficial/configure.sh b/b2g/branding/unofficial/configure.sh index d68f8ef723..127a0f1a17 100755 --- a/b2g/branding/unofficial/configure.sh +++ b/b2g/branding/unofficial/configure.sh @@ -2,6 +2,5 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -ANDROID_PACKAGE_NAME=org.mozilla.b2g_`echo $USER | sed 's/-/_/g'` MOZ_APP_DISPLAYNAME=B2G MOZ_UPDATER= diff --git a/b2g/branding/unofficial/default.png b/b2g/branding/unofficial/default.png new file mode 100644 index 0000000000..c4307fc841 Binary files /dev/null and b/b2g/branding/unofficial/default.png differ diff --git a/b2g/config/mozconfigs/common.override b/b2g/config/mozconfigs/common.override index b94b773c00..abe73b4605 100644 --- a/b2g/config/mozconfigs/common.override +++ b/b2g/config/mozconfigs/common.override @@ -5,3 +5,4 @@ # This file is included at the bottom of all b2g mozconfigs . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/b2g/config/mozconfigs/ics_armv7a_gecko/debug b/b2g/config/mozconfigs/ics_armv7a_gecko/debug index 494df42c02..1cacb8a755 100644 --- a/b2g/config/mozconfigs/ics_armv7a_gecko/debug +++ b/b2g/config/mozconfigs/ics_armv7a_gecko/debug @@ -12,7 +12,6 @@ export GONK_PRODUCT=generic ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-" ac_add_options --enable-debug-symbols ac_add_options --enable-debug -#. "$topsrcdir/build/mozconfig.cache" ENABLE_MARIONETTE=1 # Enable dump() from JS. diff --git a/b2g/config/mozconfigs/ics_armv7a_gecko/nightly b/b2g/config/mozconfigs/ics_armv7a_gecko/nightly index ece18271d8..c2f1ae0fa9 100644 --- a/b2g/config/mozconfigs/ics_armv7a_gecko/nightly +++ b/b2g/config/mozconfigs/ics_armv7a_gecko/nightly @@ -13,7 +13,6 @@ export GONK_PRODUCT=generic ac_add_options --with-gonk-toolchain-prefix="$topsrcdir/gonk-toolchain/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-linux-androideabi-4.4.x/bin/arm-linux-androideabi-" ac_add_options --enable-debug-symbols # ac_add_options --enable-profiling -#. "$topsrcdir/build/mozconfig.cache" ENABLE_MARIONETTE=1 # Enable dump() from JS. diff --git a/b2g/config/mozconfigs/linux32_gecko/debug b/b2g/config/mozconfigs/linux32_gecko/debug index 26a6668ce4..00bb23c805 100644 --- a/b2g/config/mozconfigs/linux32_gecko/debug +++ b/b2g/config/mozconfigs/linux32_gecko/debug @@ -1,3 +1,6 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 . "$topsrcdir/b2g/config/mozconfigs/common" . "$topsrcdir/build/unix/mozconfig.linux32" @@ -21,7 +24,6 @@ ac_add_options --enable-warnings-as-errors # Use sccache no_sccache= -. "$topsrcdir/build/mozconfig.cache" #B2G options ac_add_options --enable-application=b2g diff --git a/b2g/config/mozconfigs/linux32_gecko/nightly b/b2g/config/mozconfigs/linux32_gecko/nightly index 06b978d701..37541e7829 100644 --- a/b2g/config/mozconfigs/linux32_gecko/nightly +++ b/b2g/config/mozconfigs/linux32_gecko/nightly @@ -1,3 +1,7 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . "$topsrcdir/b2g/config/mozconfigs/common" . "$topsrcdir/build/unix/mozconfig.linux32" @@ -20,7 +24,6 @@ ac_add_options --enable-warnings-as-errors # Use sccache no_sccache= -. "$topsrcdir/build/mozconfig.cache" #B2G options ac_add_options --enable-application=b2g diff --git a/b2g/config/mozconfigs/linux64_gecko/debug b/b2g/config/mozconfigs/linux64_gecko/debug index c76af457be..8d16bb1851 100644 --- a/b2g/config/mozconfigs/linux64_gecko/debug +++ b/b2g/config/mozconfigs/linux64_gecko/debug @@ -1,3 +1,6 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 . "$topsrcdir/b2g/config/mozconfigs/common" . "$topsrcdir/build/unix/mozconfig.linux" @@ -21,7 +24,6 @@ ac_add_options --enable-warnings-as-errors # Use sccache no_sccache= -. "$topsrcdir/build/mozconfig.cache" #B2G options ac_add_options --enable-application=b2g diff --git a/b2g/config/mozconfigs/linux64_gecko/nightly b/b2g/config/mozconfigs/linux64_gecko/nightly index 304d4dac23..e8526b4908 100644 --- a/b2g/config/mozconfigs/linux64_gecko/nightly +++ b/b2g/config/mozconfigs/linux64_gecko/nightly @@ -1,3 +1,7 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . "$topsrcdir/b2g/config/mozconfigs/common" . "$topsrcdir/build/unix/mozconfig.linux" @@ -20,7 +24,6 @@ ac_add_options --enable-warnings-as-errors # Use sccache no_sccache= -. "$topsrcdir/build/mozconfig.cache" #B2G options ac_add_options --enable-application=b2g diff --git a/b2g/config/mozconfigs/macosx64_gecko/nightly b/b2g/config/mozconfigs/macosx64_gecko/nightly index e968e6ed34..14a7bc1960 100644 --- a/b2g/config/mozconfigs/macosx64_gecko/nightly +++ b/b2g/config/mozconfigs/macosx64_gecko/nightly @@ -1,3 +1,6 @@ +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . "$topsrcdir/b2g/config/mozconfigs/common" # Use sccache diff --git a/b2g/config/mozconfigs/win32_gecko/nightly b/b2g/config/mozconfigs/win32_gecko/nightly index 886c3bf7ee..9bb1203ae9 100644 --- a/b2g/config/mozconfigs/win32_gecko/nightly +++ b/b2g/config/mozconfigs/win32_gecko/nightly @@ -1,3 +1,7 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . "$topsrcdir/b2g/config/mozconfigs/common" ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL} diff --git a/b2g/config/tooltool-manifests/win32/releng.manifest b/b2g/config/tooltool-manifests/win32/releng.manifest index b0768e4bd5..a9c350a226 100644 --- a/b2g/config/tooltool-manifests/win32/releng.manifest +++ b/b2g/config/tooltool-manifests/win32/releng.manifest @@ -11,5 +11,12 @@ "algorithm": "sha512", "filename": "moztt.tar.bz2", "unpack": "True" +}, +{ +"size": 167175, +"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"algorithm": "sha512", +"filename": "sccache.tar.bz2", +"unpack": "True" } ] diff --git a/b2g/dev/config/mozconfigs/linux64/mulet b/b2g/dev/config/mozconfigs/linux64/mulet index df4657a343..72170696a6 100644 --- a/b2g/dev/config/mozconfigs/linux64/mulet +++ b/b2g/dev/config/mozconfigs/linux64/mulet @@ -1,3 +1,7 @@ +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly" ac_add_options --enable-application=b2g/dev diff --git a/b2g/dev/config/mozconfigs/macosx64/mulet b/b2g/dev/config/mozconfigs/macosx64/mulet index fc4afd4e8d..70f029709c 100644 --- a/b2g/dev/config/mozconfigs/macosx64/mulet +++ b/b2g/dev/config/mozconfigs/macosx64/mulet @@ -1,3 +1,8 @@ +MOZ_AUTOMATION_BUILD_SYMBOLS=0 +MOZ_AUTOMATION_PACKAGE_TESTS=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . $topsrcdir/build/macosx/mozconfig.common ac_add_options --enable-application=b2g/dev @@ -22,3 +27,4 @@ export MOZ_PACKAGE_JSSHELL=1 MOZTTDIR=$topsrcdir/moztt . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/b2g/dev/config/mozconfigs/win32/mulet b/b2g/dev/config/mozconfigs/win32/mulet index e09bff6576..4cfdf80118 100644 --- a/b2g/dev/config/mozconfigs/win32/mulet +++ b/b2g/dev/config/mozconfigs/win32/mulet @@ -1,3 +1,10 @@ +MOZ_AUTOMATION_BUILD_SYMBOLS=0 +MOZ_AUTOMATION_L10N_CHECK=0 +MOZ_AUTOMATION_PACKAGE_TESTS=0 +MOZ_AUTOMATION_INSTALLER=0 +MOZ_AUTOMATION_UPLOAD_SYMBOLS=0 +MOZ_AUTOMATION_UPDATE_PACKAGING=0 +MOZ_AUTOMATION_SDK=0 . "$topsrcdir/browser/config/mozconfigs/win32/nightly" ac_add_options --enable-application=b2g/dev diff --git a/browser/config/mozconfigs/linux32/debug b/browser/config/mozconfigs/linux32/debug index cd52d60d4b..1279757fa1 100644 --- a/browser/config/mozconfigs/linux32/debug +++ b/browser/config/mozconfigs/linux32/debug @@ -8,7 +8,6 @@ ac_add_options --enable-signmar export MOZILLA_OFFICIAL=1 #Use ccache -ac_add_options --with-ccache=/usr/bin/ccache # Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS). ac_add_options --enable-warnings-as-errors @@ -17,3 +16,4 @@ ac_add_options --enable-warnings-as-errors export MOZ_PACKAGE_JSSHELL=1 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/linux64/debug b/browser/config/mozconfigs/linux64/debug index 1fc81b7275..7900b63489 100644 --- a/browser/config/mozconfigs/linux64/debug +++ b/browser/config/mozconfigs/linux64/debug @@ -7,9 +7,6 @@ ac_add_options --enable-signmar # Needed to enable breakpad in application.ini export MOZILLA_OFFICIAL=1 -# Use ccache -ac_add_options --with-ccache=/usr/bin/ccache - # Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS). ac_add_options --enable-warnings-as-errors @@ -17,3 +14,4 @@ ac_add_options --enable-warnings-as-errors export MOZ_PACKAGE_JSSHELL=1 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/macosx-universal/beta b/browser/config/mozconfigs/macosx-universal/beta index cc2f44d0ef..80c77b484d 100644 --- a/browser/config/mozconfigs/macosx-universal/beta +++ b/browser/config/mozconfigs/macosx-universal/beta @@ -3,3 +3,4 @@ ac_add_options --enable-official-branding . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/macosx-universal/l10n-mozconfig b/browser/config/mozconfigs/macosx-universal/l10n-mozconfig index 2f88f19738..8bb8d7552c 100644 --- a/browser/config/mozconfigs/macosx-universal/l10n-mozconfig +++ b/browser/config/mozconfigs/macosx-universal/l10n-mozconfig @@ -9,3 +9,4 @@ ac_add_options --with-ccache export MOZILLA_OFFICIAL=1 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/macosx-universal/release b/browser/config/mozconfigs/macosx-universal/release index ab8a4698c9..d5e493341a 100644 --- a/browser/config/mozconfigs/macosx-universal/release +++ b/browser/config/mozconfigs/macosx-universal/release @@ -9,3 +9,4 @@ ac_add_options --enable-official-branding export BUILDING_RELEASE=1 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/macosx64/debug b/browser/config/mozconfigs/macosx64/debug index dcfbdea3e4..6d8980c50e 100644 --- a/browser/config/mozconfigs/macosx64/debug +++ b/browser/config/mozconfigs/macosx64/debug @@ -17,3 +17,4 @@ ac_add_options --enable-warnings-as-errors export MOZ_PACKAGE_JSSHELL=1 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/macosx64/l10n-mozconfig b/browser/config/mozconfigs/macosx64/l10n-mozconfig index b2cfa2119f..5c4f430029 100644 --- a/browser/config/mozconfigs/macosx64/l10n-mozconfig +++ b/browser/config/mozconfigs/macosx64/l10n-mozconfig @@ -6,3 +6,4 @@ ac_add_options --enable-update-packaging ac_add_options --with-ccache . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/win32/debug b/browser/config/mozconfigs/win32/debug index ba460ea5ac..66fe35b3cf 100644 --- a/browser/config/mozconfigs/win32/debug +++ b/browser/config/mozconfigs/win32/debug @@ -24,3 +24,4 @@ ac_add_options --enable-warnings-as-errors export MOZ_PACKAGE_JSSHELL=1 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/win64/debug b/browser/config/mozconfigs/win64/debug index d4892f4f2c..a4c52ed59b 100644 --- a/browser/config/mozconfigs/win64/debug +++ b/browser/config/mozconfigs/win64/debug @@ -23,3 +23,4 @@ export MOZ_PACKAGE_JSSHELL=1 . $topsrcdir/build/win64/mozconfig.vs2010 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/mozconfigs/win64/nightly b/browser/config/mozconfigs/win64/nightly index 59b31dface..f85fff9bd4 100644 --- a/browser/config/mozconfigs/win64/nightly +++ b/browser/config/mozconfigs/win64/nightly @@ -24,3 +24,4 @@ export MOZ_PACKAGE_JSSHELL=1 . $topsrcdir/build/win64/mozconfig.vs2010 . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest new file mode 100644 index 0000000000..4fe036e722 --- /dev/null +++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest @@ -0,0 +1,51 @@ +[ +{ +"clang_version": "r241406" +}, +{ +"size": 100307285, +"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", +"algorithm": "sha512", +"filename": "clang.tar.bz2", +"unpack": true +}, +{ +"size": 3008804, +"visibility": "public", +"digest": "ba6937f14f3d8b26dcb2d39490dee6b0a8afb60f672f5debb71d7b62c1ec52103201b4b1a3d258f945567de531384b36ddb2ce4aa73dc63d72305b11c146847c", +"algorithm": "sha512", +"unpack": true, +"filename": "cctools.tar.gz" +}, +{ +"size": 35215976, +"visibility": "internal", +"digest": "8be736545ddab25ebded188458ce974d5c9a7e29f3c50d2ebfbcb878f6aff853dd2ff5a3528bdefc64396a10101a1b50fd2fe52000140df33643cebe1ea759da", +"algorithm": "sha512", +"unpack": true, +"filename": "MacOSX10.7.sdk.tar.bz2" +}, +{ +"size": 167175, +"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831", +"algorithm": "sha512", +"unpack": true, +"filename": "sccache.tar.bz2" +}, +{ +"size": 57060, +"visibility": "public", +"digest": "9649ca595f4cf088d118da26201f92cc94cda7af49c7c48112ee31cd13c83b2935b3e145de9dd78060cff2480b4c2e7ff5fb24235876956fed13c87852071998", +"algorithm": "sha512", +"unpack": true, +"filename": "dmg.tar.xz" +}, +{ +"size": 188880, +"visibility": "public", +"digest": "1ffddd43efb03aed897ee42035d9d8d758a8d66ab6c867599ef755e1a586768fc22011ce03698af61454920b00fe8bed08c9a681e7bd324d7f8f78c026c83943", +"algorithm": "sha512", +"unpack": true, +"filename": "genisoimage.tar.xz" +} +] diff --git a/build/autoconf/clang-plugin.m4 b/build/autoconf/clang-plugin.m4 index bcd37ca6c2..5618a1c312 100644 --- a/build/autoconf/clang-plugin.m4 +++ b/build/autoconf/clang-plugin.m4 @@ -39,7 +39,7 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then LLVM_LDFLAGS=`$LLVMCONFIG --system-libs | xargs` LLVM_LDFLAGS="$LLVM_LDFLAGS `$LLVMCONFIG --ldflags --libs core mc analysis asmparser mcparser bitreader option | xargs`" - if test "${OS_ARCH}" = "Darwin"; then + if test "${HOST_OS_ARCH}" = "Darwin"; then CLANG_LDFLAGS="-lclangFrontend -lclangDriver -lclangSerialization" CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangParse -lclangSema -lclangAnalysis" CLANG_LDFLAGS="$CLANG_LDFLAGS -lclangEdit -lclangAST -lclangLex" diff --git a/build/autoconf/nspr-build.m4 b/build/autoconf/nspr-build.m4 index 7e63228dc8..f3945274d1 100644 --- a/build/autoconf/nspr-build.m4 +++ b/build/autoconf/nspr-build.m4 @@ -157,11 +157,11 @@ if test -n "$MOZ_NATIVE_NSPR" -o -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it])) CFLAGS=$_SAVE_CFLAGS elif test -z "$JS_POSIX_NSPR"; then - NSPR_CFLAGS="-I${LIBXUL_DIST}/include/nspr" + NSPR_CFLAGS="-I${DIST}/include/nspr" if test -n "$GNU_CC"; then - NSPR_LIBS="-L${LIBXUL_DIST}/lib -lnspr${NSPR_VERSION} -lplc${NSPR_VERSION} -lplds${NSPR_VERSION}" + NSPR_LIBS="-L${DIST}/lib -lnspr${NSPR_VERSION} -lplc${NSPR_VERSION} -lplds${NSPR_VERSION}" else - NSPR_LIBS="${LIBXUL_DIST}/lib/nspr${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plc${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plds${NSPR_VERSION}.lib " + NSPR_LIBS="${DIST}/lib/nspr${NSPR_VERSION}.lib ${DIST}/lib/plc${NSPR_VERSION}.lib ${DIST}/lib/plds${NSPR_VERSION}.lib " fi fi diff --git a/build/automation-build.mk b/build/automation-build.mk index dd9260b171..554c73e882 100644 --- a/build/automation-build.mk +++ b/build/automation-build.mk @@ -13,7 +13,7 @@ _CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs AUTOMATION_PPARGS = \ -DBROWSER_PATH=$(browser_path) \ - -DXPC_BIN_PATH='"$(LIBXUL_DIST)/bin"' \ + -DXPC_BIN_PATH='"$(DIST)/bin"' \ -DBIN_SUFFIX='"$(BIN_SUFFIX)"' \ -DPROFILE_DIR='"$(_PROFILE_DIR)"' \ -DCERTS_SRC_DIR='"$(_CERTS_SRC_DIR)"' \ diff --git a/build/clang-plugin/Makefile.in b/build/clang-plugin/Makefile.in index 51b31ef4f6..3bc0baa2bb 100644 --- a/build/clang-plugin/Makefile.in +++ b/build/clang-plugin/Makefile.in @@ -14,6 +14,17 @@ OS_COMPILE_CXXFLAGS := OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS) DSO_LDOPTS := -shared +ifeq ($(HOST_OS_ARCH)_$(OS_ARCH),Linux_Darwin) +# Use the host compiler instead of the target compiler. +CXX := $(HOST_CXX) +# Don't use --uselist with expandlibs_exec.py. +EXPAND_MKSHLIB_ARGS := +# expandlibs doesn't know the distinction between host and target toolchains, +# and on cross linux/darwin builds, the options to give to the linker for file +# lists differ between both, so don't use file lists. +MOZ_FIX_LINK_PATHS := +endif + # Use the default OS X deployment target to enable using the libc++ headers # correctly. Note that the binary produced here is a host tool and doesn't need # to be distributed. diff --git a/build/gen_mach_buildprops.py b/build/gen_mach_buildprops.py index 16a8795ce8..0055aa2573 100644 --- a/build/gen_mach_buildprops.py +++ b/build/gen_mach_buildprops.py @@ -53,6 +53,7 @@ def getUrlProperties(filename, package): ('completeMarUrl', lambda m: m.endswith('.complete.mar')), ('partialMarUrl', lambda m: m.endswith('.mar') and '.partial.' in m), ('codeCoverageURL', lambda m: m.endswith('code-coverage-gcno.zip')), + ('sdkUrl', lambda m: m.endswith(('sdk.tar.bz2', 'sdk.zip'))), ('testPackagesUrl', lambda m: m.endswith('test_packages.json')), ('packageUrl', lambda m: m.endswith(package)), ] diff --git a/build/macosx/cross-mozconfig.common b/build/macosx/cross-mozconfig.common new file mode 100644 index 0000000000..6247ca1d1c --- /dev/null +++ b/build/macosx/cross-mozconfig.common @@ -0,0 +1,52 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOZ_AUTOMATION_L10N_CHECK=0 + +if [ "x$IS_NIGHTLY" = "xyes" ]; then + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} +fi +. "$topsrcdir/build/mozconfig.common" + +# ld needs libLTO.so from llvm +mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib" + +CROSS_CCTOOLS_PATH=$topsrcdir/cctools +CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk +CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks +FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT" + +export CC="$topsrcdir/clang/bin/clang $FLAGS" +export CXX="$topsrcdir/clang/bin/clang++ $FLAGS" +export CPP="$topsrcdir/clang/bin/clang $FLAGS -E" +export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config +export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip" +export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10- +#TODO: bug 1184202 - would be nice if these could be detected with TOOLCHAIN_PREFIX automatically +export AR=${TOOLCHAIN_PREFIX}ar +export RANLIB=${TOOLCHAIN_PREFIX}ranlib +export STRIP=${TOOLCHAIN_PREFIX}strip +export OTOOL=${TOOLCHAIN_PREFIX}otool +export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil +export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage +export DMG_TOOL=$topsrcdir/dmg/dmg + +export HOST_CC="$topsrcdir/clang/bin/clang" +export HOST_CXX="$topsrcdir/clang/bin/clang++" +export HOST_CPP="$topsrcdir/clang/bin/clang -E" +export HOST_CFLAGS="-g" +export HOST_CXXFLAGS="-g" +export HOST_LDFLAGS="-g" + +ac_add_options --target=x86_64-apple-darwin +ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS + +# Enable static analysis checks by default on OSX cross builds. +ac_add_options --enable-clang-plugin + +. "$topsrcdir/build/mozconfig.cache" + +export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token diff --git a/build/macosx/local-mozconfig.common b/build/macosx/local-mozconfig.common new file mode 100644 index 0000000000..a36cf895d4 --- /dev/null +++ b/build/macosx/local-mozconfig.common @@ -0,0 +1,37 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOZ_AUTOMATION_L10N_CHECK=0 + +if [ "x$IS_NIGHTLY" = "xyes" ]; then + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} +fi +. "$topsrcdir/build/mozconfig.common" + +if [ -d "$topsrcdir/clang" ]; then + # mozilla-central based build + export CC=$topsrcdir/clang/bin/clang + export CXX=$topsrcdir/clang/bin/clang++ + export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config +elif [ -d "$topsrcdir/../clang" ]; then + # comm-central based build + export CC=$topsrcdir/../clang/bin/clang + export CXX=$topsrcdir/../clang/bin/clang++ + export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config +fi + +# If not set use the system default clang +if [ -z "$CC" ]; then + export CC=clang +fi + +# If not set use the system default clang++ +if [ -z "$CXX" ]; then + export CXX=clang++ +fi + +export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token diff --git a/build/macosx/mozconfig.common b/build/macosx/mozconfig.common index 83d680490c..27634b7f31 100644 --- a/build/macosx/mozconfig.common +++ b/build/macosx/mozconfig.common @@ -1,37 +1,5 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -MOZ_AUTOMATION_L10N_CHECK=0 - -if [ "x$IS_NIGHTLY" = "xyes" ]; then - MOZ_AUTOMATION_UPLOAD_SYMBOLS=1 - MOZ_AUTOMATION_UPDATE_PACKAGING=1 +if test `uname -s` = Linux; then + . $topsrcdir/build/macosx/cross-mozconfig.common +else + . $topsrcdir/build/macosx/local-mozconfig.common fi -. "$topsrcdir/build/mozconfig.common" - -if [ -d "$topsrcdir/clang" ]; then - # mozilla-central based build - export CC=$topsrcdir/clang/bin/clang - export CXX=$topsrcdir/clang/bin/clang++ - export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config -elif [ -d "$topsrcdir/../clang" ]; then - # comm-central based build - export CC=$topsrcdir/../clang/bin/clang - export CXX=$topsrcdir/../clang/bin/clang++ - export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config -fi - -# If not set use the system default clang -if [ -z "$CC" ]; then - export CC=clang -fi - -# If not set use the system default clang++ -if [ -z "$CXX" ]; then - export CXX=clang++ -fi - -. "$topsrcdir/build/mozconfig.cache" - -export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token diff --git a/build/mozconfig.cache b/build/mozconfig.cache index 56bb6bce91..2edafac787 100644 --- a/build/mozconfig.cache +++ b/build/mozconfig.cache @@ -51,6 +51,10 @@ if test -z "$bucket"; then ac_add_options --with-ccache esac else + if ! test -e $topsrcdir/sccache/sccache.py; then + echo "Sccache missing in the tooltool manifest" >&2 + exit 1 + fi mk_add_options "export SCCACHE_BUCKET=$bucket" case "$master" in *use1.mozilla.com*|*usw2.mozilla.com*) diff --git a/build/mozconfig.win-common b/build/mozconfig.win-common index 6eec310362..3f040197d0 100644 --- a/build/mozconfig.win-common +++ b/build/mozconfig.win-common @@ -3,8 +3,10 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. if [ "x$IS_NIGHTLY" = "xyes" ]; then - MOZ_AUTOMATION_UPLOAD_SYMBOLS=1 - MOZ_AUTOMATION_UPDATE_PACKAGING=1 + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} fi MOZ_AUTOMATION_INSTALLER=1 diff --git a/build/package/mac_osx/pkg-dmg b/build/package/mac_osx/pkg-dmg deleted file mode 100755 index d3b27da7dc..0000000000 --- a/build/package/mac_osx/pkg-dmg +++ /dev/null @@ -1,1488 +0,0 @@ -#!/usr/bin/perl -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -use strict; -use warnings; - -=pod - -=head1 NAME - -B - Mac OS X disk image (.dmg) packager - -=head1 SYNOPSIS - -B -B<--source> I -B<--target> I -[B<--format> I] -[B<--volname> I] -[B<--tempdir> I] -[B<--mkdir> I] -[B<--copy> I[:I]] -[B<--symlink> I[:I]] -[B<--license> I] -[B<--resource> I] -[B<--icon> I] -[B<--attribute> I:I[:I...] -[B<--idme>] -[B<--sourcefile>] -[B<--verbosity> I] -[B<--dry-run>] - -=head1 DESCRIPTION - -I takes a directory identified by I and transforms -it into a disk image stored as I. The disk image will -occupy the least space possible for its format, or the least space that the -authors have been able to figure out how to achieve. - -=head1 OPTIONS - -=over 5 - -==item B<--source> I - -Identifies the directory that will be packaged up. This directory is not -touched, a copy will be made in a temporary directory for staging purposes. -See B<--tempdir>. - -==item B<--target> I - -The disk image to create. If it exists and is not in use, it will be -overwritten. If I already contains a suitable extension, -it will be used unmodified. If no extension is present, or the extension -is incorrect for the selected format, the proper extension will be added. -See B<--format>. - -==item B<--format> I - -The format to create the disk image in. Valid values for I are: - - UDZO - zlib-compressed, read-only; extension I<.dmg> - - UDBZ - bzip2-compressed, read-only; extension I<.dmg>; - create and use on 10.4 ("Tiger") and later only - - UDRW - read-write; extension I<.dmg> - - UDSP - read-write, sparse; extension I<.sparseimage> - -UDBZ is the default format. - -See L for a description of these formats. - -=item B<--volname> I - -The name of the volume in the disk image. If not specified, I -defaults to the name of the source directory from B<--source>. - -=item B<--tempdir> I - -A temporary directory to stage intermediate files in. I must -have enough space available to accommodate twice the size of the files -being packaged. If not specified, defaults to the same directory that -the I is to be placed in. B will remove any -temporary files it places in I. - -=item B<--mkdir> I - -Specifies a directory that should be created in the disk image. -I and any ancestor directories will be created. This is -useful in conjunction with B<--copy>, when copying files to directories -that may not exist in I. B<--mkdir> may appear multiple -times. - -=item B<--copy> I[:I] - -Additional files to copy into the disk image. If I is -specified, I is copied to the location I identifies, -otherwise, I is copied to the root of the new volume. B<--copy> -provides a way to package up a I by adding files to it -without modifying the original I. B<--copy> may appear -multiple times. - -This option is useful for adding .DS_Store files and window backgrounds -to disk images. - -=item B<--symlink> I[:I] - -Like B<--copy>, but allows symlinks to point out of the volume. Empty symlink -destinations are interpreted as "like the source path, but inside the dmg" - -This option is useful for adding symlinks to external resources, -e.g. to /Applications. - -=item B<--license> I - -A plain text file containing a license agreement to be displayed before -the disk image is mounted. English is the only supported language. To -include license agreements in other languages, in multiple languages, -or to use formatted text, prepare a resource and use L<--resource>. - -=item B<--resource> I - -A resource file to merge into I. If I is UDZO or -UDBZ, the disk image will be flattened to a single-fork file that contains -the resource but may be freely transferred without any special encodings. -I must be in a format suitable for L. See L for a -description of the format, and L for a discussion on flattened -disk images. B<--resource> may appear multiple times. - -This option is useful for adding license agreements and other messages -to disk images. - -=item B<--icon> I - -Specifies an I file that will be used as the icon for the root of -the volume. This file will be copied to the new volume and the custom -icon attribute will be set on the root folder. - -=item B<--attribute> I:I[:I...] - -Sets the attributes of I to the attribute list in I. See -L - -=item B<--idme> - -Enable IDME to make the disk image "Internet-enabled." The first time -the image is mounted, if IDME processing is enabled on the system, the -contents of the image will be copied out of the image and the image will -be placed in the trash with IDME disabled. - -=item B<--sourcefile> - -If this option is present, I is treated as a file, and is -placed as a file within the volume's root folder. Without this option, -I is treated as the volume root itself. - -=item B<--verbosity> I - -Adjusts the level of loudness of B. The possible values for -I are: - 0 - Only error messages are displayed. - 1 - Print error messages and command invocations. - 2 - Print everything, including command output. - -The default I is 2. - -=item B<--dry-run> - -When specified, the commands that would be executed are printed, without -actually executing them. When commands depend on the output of previous -commands, dummy values are displayed. - -=back - -=head1 NON-OPTIONS - -=over 5 - -=item - -Resource forks aren't copied. - -=item - -The root folder of the created volume is designated as the folder -to open when the volume is mounted. See L. - -=item - -All files in the volume are set to be world-readable, only writable -by the owner, and world-executable when appropriate. All other -permissions bits are cleared. - -=item - -When possible, disk images are created without any partition tables. This -is what L refers to as I<-layout NONE>, and saves a handful of -kilobytes. The alternative, I, contains a partition table that -is not terribly handy on disk images that are not intended to represent any -physical disk. - -=item - -Read-write images are created with journaling off. Any read-write image -created by this tool is expected to be transient, and the goal of this tool -is to create images which consume a minimum of space. - -=back - -=head1 EXAMPLE - -pkg-dmg --source /Applications/DeerPark.app --target ~/DeerPark.dmg - --sourcefile --volname DeerPark --icon ~/DeerPark.icns - --mkdir /.background - --copy DeerParkBackground.png:/.background/background.png - --copy DeerParkDSStore:/.DS_Store - --symlink /Applications:"/Drag to here" - -=head1 REQUIREMENTS - -I has been tested with Mac OS X releases 10.2 ("Jaguar") -through 10.4 ("Tiger"). Certain adjustments to behavior are made -depending on the host system's release. Mac OS X 10.3 ("Panther") or -later are recommended. - -=head1 LICENSE - -MPL 2. - -=head1 AUTHOR - -Mark Mentovai - -=head1 SEE ALSO - -L, L, L, L, L, -L, L - -=cut - -use Fcntl; -use POSIX; -use Getopt::Long; - -sub argumentEscape(@); -sub cleanupDie($); -sub command(@); -sub commandInternal($@); -sub commandInternalVerbosity($$@); -sub commandOutput(@); -sub commandOutputVerbosity($@); -sub commandVerbosity($@); -sub copyFiles($@); -sub diskImageMaker($$$$$$$$); -sub giveExtension($$); -sub hdidMountImage($@); -sub isFormatCompressed($); -sub licenseMaker($$); -sub pathSplit($); -sub setAttributes($@); -sub trapSignal($); -sub usage(); - -# Variables used as globals -my(@gCleanup, %gConfig, $gDarwinMajor, $gDryRun, $gVerbosity); - -# Use the commands by name if they're expected to be in the user's -# $PATH (/bin:/sbin:/usr/bin:/usr/sbin). Otherwise, go by absolute -# path. These may be overridden with --config. -%gConfig = ('cmd_bless' => 'bless', - 'cmd_chmod' => 'chmod', - 'cmd_diskutil' => 'diskutil', - 'cmd_du' => 'du', - 'cmd_hdid' => 'hdid', - 'cmd_hdiutil' => 'hdiutil', - 'cmd_mkdir' => 'mkdir', - 'cmd_mktemp' => 'mktemp', - 'cmd_Rez' => 'Rez', - 'cmd_rm' => 'rm', - 'cmd_rsync' => 'rsync', - 'cmd_SetFile' => 'SetFile', - - # create_directly indicates whether hdiutil create supports - # -srcfolder and -srcdevice. It does on >= 10.3 (Panther). - # This is fixed up for earlier systems below. If false, - # hdiutil create is used to create empty disk images that - # are manually filled. - 'create_directly' => 1, - - # If hdiutil attach -mountpoint exists, use it to avoid - # mounting disk images in the default /Volumes. This reduces - # the likelihood that someone will notice a mounted image and - # interfere with it. Only available on >= 10.3 (Panther), - # fixed up for earlier systems below. - # - # This is presently turned off for all systems, because there - # is an infrequent synchronization problem during ejection. - # diskutil eject might return before the image is actually - # unmounted. If pkg-dmg then attempts to clean up its - # temporary directory, it could remove items from a read-write - # disk image or attempt to remove items from a read-only disk - # image (or a read-only item from a read-write image) and fail, - # causing pkg-dmg to abort. This problem is experienced - # under Tiger, which appears to eject asynchronously where - # previous systems treated it as a synchronous operation. - # Using hdiutil attach -mountpoint didn't always keep images - # from showing up on the desktop anyway. - 'hdiutil_mountpoint' => 0, - - # hdiutil makehybrid results in optimized disk images that - # consume less space and mount more quickly. Use it when - # it's available, but that's only on >= 10.3 (Panther). - # If false, hdiutil create is used instead. Fixed up for - # earlier systems below. - 'makehybrid' => 1, - - # hdiutil create doesn't allow specifying a folder to open - # at volume mount time, so those images are mounted and - # their root folders made holy with bless -openfolder. But - # only on >= 10.3 (Panther). Earlier systems are out of luck. - # Even on Panther, bless refuses to run unless root. - # Fixed up below. - 'openfolder_bless' => 1, - - # It's possible to save a few more kilobytes by including the - # partition only without any partition table in the image. - # This is a good idea on any system, so turn this option off. - # - # Except it's buggy. "-layout NONE" seems to be creating - # disk images with more data than just the partition table - # stripped out. You might wind up losing the end of the - # filesystem - the last file (or several) might be incomplete. - 'partition_table' => 1, - - # To create a partition table-less image from something - # created by makehybrid, the hybrid image needs to be - # mounted and a new image made from the device associated - # with the relevant partition. This requires >= 10.4 - # (Tiger), presumably because earlier systems have - # problems creating images from devices themselves attached - # to images. If this is false, makehybrid images will - # have partition tables, regardless of the partition_table - # setting. Fixed up for earlier systems below. - 'recursive_access' => 1); - -# --verbosity -$gVerbosity = 2; - -# --dry-run -$gDryRun = 0; - -# %gConfig fix-ups based on features and bugs present in certain releases. -my($ignore, $uname_r, $uname_s); -($uname_s, $ignore, $uname_r, $ignore, $ignore) = POSIX::uname(); -if($uname_s eq 'Darwin') { - ($gDarwinMajor, $ignore) = split(/\./, $uname_r, 2); - - # $major is the Darwin major release, which for our purposes, is 4 higher - # than the interesting digit in a Mac OS X release. - if($gDarwinMajor <= 6) { - # <= 10.2 (Jaguar) - # hdiutil create does not support -srcfolder or -srcdevice - $gConfig{'create_directly'} = 0; - # hdiutil attach does not support -mountpoint - $gConfig{'hdiutil_mountpoint'} = 0; - # hdiutil mkhybrid does not exist - $gConfig{'makehybrid'} = 0; - } - if($gDarwinMajor <= 7) { - # <= 10.3 (Panther) - # Can't mount a disk image and then make a disk image from the device - $gConfig{'recursive_access'} = 0; - # bless does not support -openfolder on 10.2 (Jaguar) and must run - # as root under 10.3 (Panther) - $gConfig{'openfolder_bless'} = 0; - } -} -else { - # If it's not Mac OS X, just assume all of those good features are - # available. They're not, but things will fail long before they - # have a chance to make a difference. - # - # Now, if someone wanted to document some of these private formats... - print STDERR ($0.": warning, not running on Mac OS X, ". - "this could be interesting.\n"); -} - -# Non-global variables used in Getopt -my(@attributes, @copyFiles, @createSymlinks, $iconFile, $idme, $licenseFile, - @makeDirs, $outputFormat, @resourceFiles, $sourceFile, $sourceFolder, - $targetImage, $tempDir, $volumeName); - -# --format -$outputFormat = 'UDBZ'; - -# --idme -$idme = 0; - -# --sourcefile -$sourceFile = 0; - -# Leaving this might screw up the Apple tools. -delete $ENV{'NEXT_ROOT'}; - -# This script can get pretty messy, so trap a few signals. -$SIG{'INT'} = \&trapSignal; -$SIG{'HUP'} = \&trapSignal; -$SIG{'TERM'} = \&trapSignal; - -Getopt::Long::Configure('pass_through'); -GetOptions('source=s' => \$sourceFolder, - 'target=s' => \$targetImage, - 'volname=s' => \$volumeName, - 'format=s' => \$outputFormat, - 'tempdir=s' => \$tempDir, - 'mkdir=s' => \@makeDirs, - 'copy=s' => \@copyFiles, - 'symlink=s' => \@createSymlinks, - 'license=s' => \$licenseFile, - 'resource=s' => \@resourceFiles, - 'icon=s' => \$iconFile, - 'attribute=s' => \@attributes, - 'idme' => \$idme, - 'sourcefile' => \$sourceFile, - 'verbosity=i' => \$gVerbosity, - 'dry-run' => \$gDryRun, - 'config=s' => \%gConfig); # "hidden" option not in usage() - -if(@ARGV) { - # All arguments are parsed by Getopt - usage(); - exit(1); -} - -if($gVerbosity<0 || $gVerbosity>2) { - usage(); - exit(1); -} - -if(!defined($sourceFolder) || $sourceFolder eq '' || - !defined($targetImage) || $targetImage eq '') { - # --source and --target are required arguments - usage(); - exit(1); -} - -# Make sure $sourceFolder doesn't contain trailing slashes. It messes with -# rsync. -while(substr($sourceFolder, -1) eq '/') { - chop($sourceFolder); -} - -if(!defined($volumeName)) { - # Default volumeName is the name of the source directory. - my(@components); - @components = pathSplit($sourceFolder); - $volumeName = pop(@components); -} - -my(@tempDirComponents, $targetImageFilename); -@tempDirComponents = pathSplit($targetImage); -$targetImageFilename = pop(@tempDirComponents); - -if(defined($tempDir)) { - @tempDirComponents = pathSplit($tempDir); -} -else { - # Default tempDir is the same directory as what is specified for - # targetImage - $tempDir = join('/', @tempDirComponents); -} - -# Ensure that the path of the target image has a suitable extension. If -# it didn't, hdiutil would add one, and we wouldn't be able to find the -# file. -# -# Note that $targetImageFilename is not being reset. This is because it's -# used to build other names below, and we don't need to be adding all sorts -# of extra unnecessary extensions to the name. -my($originalTargetImage, $requiredExtension); -$originalTargetImage = $targetImage; -if($outputFormat eq 'UDSP') { - $requiredExtension = '.sparseimage'; -} -else { - $requiredExtension = '.dmg'; -} -$targetImage = giveExtension($originalTargetImage, $requiredExtension); - -if($targetImage ne $originalTargetImage) { - print STDERR ($0.": warning: target image extension is being added\n"); - print STDERR (' The new filename is '. - giveExtension($targetImageFilename,$requiredExtension)."\n"); -} - -# Make a temporary directory in $tempDir for our own nefarious purposes. -my(@output, $tempSubdir, $tempSubdirTemplate); -$tempSubdirTemplate=join('/', @tempDirComponents, - 'pkg-dmg.'.$$.'.XXXXXXXX'); -if(!(@output = commandOutput($gConfig{'cmd_mktemp'}, '-d', - $tempSubdirTemplate)) || $#output != 0) { - cleanupDie('mktemp failed'); -} - -if($gDryRun) { - (@output)=($tempSubdirTemplate); -} - -($tempSubdir) = @output; - -push(@gCleanup, - sub {commandVerbosity(0, $gConfig{'cmd_rm'}, '-rf', $tempSubdir);}); - -my($tempMount, $tempRoot, @tempsToMake); -$tempRoot = $tempSubdir.'/stage'; -$tempMount = $tempSubdir.'/mount'; -push(@tempsToMake, $tempRoot); -if($gConfig{'hdiutil_mountpoint'}) { - push(@tempsToMake, $tempMount); -} - -if(command($gConfig{'cmd_mkdir'}, @tempsToMake) != 0) { - cleanupDie('mkdir tempRoot/tempMount failed'); -} - -# This cleanup object is not strictly necessary, because $tempRoot is inside -# of $tempSubdir, but the rest of the script relies on this object being -# on the cleanup stack and expects to remove it. -push(@gCleanup, - sub {commandVerbosity(0, $gConfig{'cmd_rm'}, '-rf', $tempRoot);}); - -# If $sourceFile is true, it means that $sourceFolder is to be treated as -# a file and placed as a file within the volume root, as opposed to being -# treated as the volume root itself. rsync will do this by default, if no -# trailing '/' is present. With a trailing '/', $sourceFolder becomes -# $tempRoot, instead of becoming an entry in $tempRoot. -if(command($gConfig{'cmd_rsync'}, '-a', '--copy-unsafe-links', - $sourceFolder.($sourceFile?'':'/'),$tempRoot) != 0) { - cleanupDie('rsync failed'); -} - -if(@makeDirs) { - my($makeDir, @tempDirsToMake); - foreach $makeDir (@makeDirs) { - if($makeDir =~ /^\//) { - push(@tempDirsToMake, $tempRoot.$makeDir); - } - else { - push(@tempDirsToMake, $tempRoot.'/'.$makeDir); - } - } - if(command($gConfig{'cmd_mkdir'}, '-p', @tempDirsToMake) != 0) { - cleanupDie('mkdir failed'); - } -} - -# copy files and/or create symlinks -copyFiles($tempRoot, 'copy', @copyFiles); -copyFiles($tempRoot, 'symlink', @createSymlinks); - -if($gConfig{'create_directly'}) { - # If create_directly is false, the contents will be rsynced into a - # disk image and they would lose their attributes. - setAttributes($tempRoot, @attributes); -} - -if(defined($iconFile)) { - if(command($gConfig{'cmd_rsync'}, '-a', '--copy-unsafe-links', $iconFile, - $tempRoot.'/.VolumeIcon.icns') != 0) { - cleanupDie('rsync failed for volume icon'); - } - - # It's pointless to set the attributes of the root when diskutil create - # -srcfolder is being used. In that case, the attributes will be set - # later, after the image is already created. - if(isFormatCompressed($outputFormat) && - (command($gConfig{'cmd_SetFile'}, '-a', 'C', $tempRoot) != 0)) { - cleanupDie('SetFile failed'); - } -} - -if(command($gConfig{'cmd_chmod'}, '-R', 'a+rX,a-st,u+w,go-w', - $tempRoot) != 0) { - cleanupDie('chmod failed'); -} - -my($unflattenable); -if(isFormatCompressed($outputFormat)) { - $unflattenable = 1; -} -else { - $unflattenable = 0; -} - -diskImageMaker($tempRoot, $targetImage, $outputFormat, $volumeName, - $tempSubdir, $tempMount, $targetImageFilename, defined($iconFile)); - -if(defined($licenseFile) && $licenseFile ne '') { - my($licenseResource); - $licenseResource = $tempSubdir.'/license.r'; - if(!licenseMaker($licenseFile, $licenseResource)) { - cleanupDie('licenseMaker failed'); - } - push(@resourceFiles, $licenseResource); - # Don't add a cleanup object because licenseResource is in tempSubdir. -} - -if(@resourceFiles) { - # Add resources, such as a license agreement. - - # Only unflatten read-only and compressed images. It's not supported - # on other image times. - if($unflattenable && - (command($gConfig{'cmd_hdiutil'}, 'unflatten', $targetImage)) != 0) { - cleanupDie('hdiutil unflatten failed'); - } - # Don't push flatten onto the cleanup stack. If we fail now, we'll be - # removing $targetImage anyway. - - # Type definitions come from Carbon.r. - if(command($gConfig{'cmd_Rez'}, 'Carbon.r', @resourceFiles, '-a', '-o', - $targetImage) != 0) { - cleanupDie('Rez failed'); - } - - # Flatten. This merges the resource fork into the data fork, so no - # special encoding is needed to transfer the file. - if($unflattenable && - (command($gConfig{'cmd_hdiutil'}, 'flatten', $targetImage)) != 0) { - cleanupDie('hdiutil flatten failed'); - } -} - -# $tempSubdir is no longer needed. It's buried on the stack below the -# rm of the fresh image file. Splice in this fashion is equivalent to -# pop-save, pop, push-save. -splice(@gCleanup, -2, 1); -# No need to remove licenseResource separately, it's in tempSubdir. -if(command($gConfig{'cmd_rm'}, '-rf', $tempSubdir) != 0) { - cleanupDie('rm -rf tempSubdir failed'); -} - -if($idme) { - if(command($gConfig{'cmd_hdiutil'}, 'internet-enable', '-yes', - $targetImage) != 0) { - cleanupDie('hdiutil internet-enable failed'); - } -} - -# Done. - -exit(0); - -# argumentEscape(@arguments) -# -# Takes a list of @arguments and makes them shell-safe. -sub argumentEscape(@) { - my(@arguments); - @arguments = @_; - my($argument, @argumentsOut); - foreach $argument (@arguments) { - $argument =~ s%([^A-Za-z0-9_\-/.=+,])%\\$1%g; - push(@argumentsOut, $argument); - } - return @argumentsOut; -} - -# cleanupDie($message) -# -# Displays $message as an error message, and then runs through the -# @gCleanup stack, performing any cleanup operations needed before -# exiting. Does not return, exits with exit status 1. -sub cleanupDie($) { - my($message); - ($message) = @_; - print STDERR ($0.': '.$message.(@gCleanup?' (cleaning up)':'')."\n"); - while(@gCleanup) { - my($subroutine); - $subroutine = pop(@gCleanup); - &$subroutine; - } - exit(1); -} - -# command(@arguments) -# -# Runs the specified command at the verbosity level defined by $gVerbosity. -# Returns nonzero on failure, returning the exit status if appropriate. -# Discards command output. -sub command(@) { - my(@arguments); - @arguments = @_; - return commandVerbosity($gVerbosity,@arguments); -} - -# commandInternal($command, @arguments) -# -# Runs the specified internal command at the verbosity level defined by -# $gVerbosity. -# Returns zero(!) on failure, because commandInternal is supposed to be a -# direct replacement for the Perl system call wrappers, which, unlike shell -# commands and C equivalent system calls, return true (instead of 0) to -# indicate success. -sub commandInternal($@) { - my(@arguments, $command); - ($command, @arguments) = @_; - return commandInternalVerbosity($gVerbosity, $command, @arguments); -} - -# commandInternalVerbosity($verbosity, $command, @arguments) -# -# Run an internal command, printing a bogus command invocation message if -# $verbosity is true. -# -# If $command is unlink: -# Removes the files specified by @arguments. Wraps unlink. -# -# If $command is symlink: -# Creates the symlink specified by @arguments. Wraps symlink. -sub commandInternalVerbosity($$@) { - my(@arguments, $command, $verbosity); - ($verbosity, $command, @arguments) = @_; - if($command eq 'unlink') { - if($verbosity || $gDryRun) { - print(join(' ', 'rm', '-f', argumentEscape(@arguments))."\n"); - } - if($gDryRun) { - return $#arguments+1; - } - return unlink(@arguments); - } - elsif($command eq 'symlink') { - if($verbosity || $gDryRun) { - print(join(' ', 'ln', '-s', argumentEscape(@arguments))."\n"); - } - if($gDryRun) { - return 1; - } - my($source, $target); - ($source, $target) = @arguments; - return symlink($source, $target); - } -} - -# commandOutput(@arguments) -# -# Runs the specified command at the verbosity level defined by $gVerbosity. -# Output is returned in an array of lines. undef is returned on failure. -# The exit status is available in $?. -sub commandOutput(@) { - my(@arguments); - @arguments = @_; - return commandOutputVerbosity($gVerbosity, @arguments); -} - -# commandOutputVerbosity($verbosity, @arguments) -# -# Runs the specified command at the verbosity level defined by the -# $verbosity argument. Output is returned in an array of lines. undef is -# returned on failure. The exit status is available in $?. -# -# If an error occurs in fork or exec, an error message is printed to -# stderr and undef is returned. -# -# If $verbosity is 0, the command invocation is not printed, and its -# stdout is not echoed back to stdout. -# -# If $verbosity is 1, the command invocation is printed. -# -# If $verbosity is 2, the command invocation is printed and the output -# from stdout is echoed back to stdout. -# -# Regardless of $verbosity, stderr is left connected. -sub commandOutputVerbosity($@) { - my(@arguments, $verbosity); - ($verbosity, @arguments) = @_; - my($pid); - if($verbosity || $gDryRun) { - print(join(' ', argumentEscape(@arguments))."\n"); - } - if($gDryRun) { - return(1); - } - if (!defined($pid = open(*COMMAND, '-|'))) { - printf STDERR ($0.': fork: '.$!."\n"); - return undef; - } - elsif ($pid) { - # parent - my(@lines); - while(!eof(*COMMAND)) { - my($line); - chop($line = ); - if($verbosity > 1) { - print($line."\n"); - } - push(@lines, $line); - } - close(*COMMAND); - if ($? == -1) { - printf STDERR ($0.': fork: '.$!."\n"); - return undef; - } - elsif ($? & 127) { - printf STDERR ($0.': exited on signal '.($? & 127). - ($? & 128 ? ', core dumped' : '')."\n"); - return undef; - } - return @lines; - } - else { - # child; this form of exec is immune to shell games - if(!exec {$arguments[0]} (@arguments)) { - printf STDERR ($0.': exec: '.$!."\n"); - exit(-1); - } - } -} - -# commandVerbosity($verbosity, @arguments) -# -# Runs the specified command at the verbosity level defined by the -# $verbosity argument. Returns nonzero on failure, returning the exit -# status if appropriate. Discards command output. -sub commandVerbosity($@) { - my(@arguments, $verbosity); - ($verbosity, @arguments) = @_; - if(!defined(commandOutputVerbosity($verbosity, @arguments))) { - return -1; - } - return $?; -} - -# copyFiles($tempRoot, $method, @arguments) -# -# Copies files or create symlinks in the disk image. -# See --copy and --symlink descriptions for details. -# If $method is 'copy', @arguments are interpreted as source:target, if $method -# is 'symlink', @arguments are interpreted as symlink:target. -sub copyFiles($@) { - my(@fileList, $method, $tempRoot); - ($tempRoot, $method, @fileList) = @_; - my($file, $isSymlink); - $isSymlink = ($method eq 'symlink'); - foreach $file (@fileList) { - my($source, $target); - ($source, $target) = split(/:/, $file); - if(!defined($target) and $isSymlink) { - # empty symlink targets would result in an invalid target and fail, - # but they shall be interpreted as "like source path, but inside dmg" - $target = $source; - } - if(!defined($target)) { - $target = $tempRoot; - } - elsif($target =~ /^\//) { - $target = $tempRoot.$target; - } - else { - $target = $tempRoot.'/'.$target; - } - - my($success); - if($isSymlink) { - $success = commandInternal('symlink', $source, $target); - } - else { - $success = !command($gConfig{'cmd_rsync'}, '-a', '--copy-unsafe-links', - $source, $target); - } - if(!$success) { - cleanupDie('copyFiles failed for method '.$method); - } - } -} - -# diskImageMaker($source, $destination, $format, $name, $tempDir, $tempMount, -# $baseName, $setRootIcon) -# -# Creates a disk image in $destination of format $format corresponding to the -# source directory $source. $name is the volume name. $tempDir is a good -# place to write temporary files, which should be empty (aside from the other -# things that this script might create there, like stage and mount). -# $tempMount is a mount point for temporary disk images. $baseName is the -# name of the disk image, and is presently unused. $setRootIcon is true if -# a volume icon was added to the staged $source and indicates that the -# custom volume icon bit on the volume root needs to be set. -sub diskImageMaker($$$$$$$$) { - my($baseName, $destination, $format, $name, $setRootIcon, $source, - $tempDir, $tempMount); - ($source, $destination, $format, $name, $tempDir, $tempMount, - $baseName, $setRootIcon) = @_; - if(isFormatCompressed($format)) { - my($uncompressedImage); - - if($gConfig{'makehybrid'}) { - my($hybridImage); - $hybridImage = giveExtension($tempDir.'/hybrid', '.dmg'); - - if(command($gConfig{'cmd_hdiutil'}, 'makehybrid', '-hfs', - '-hfs-volume-name', $name, '-hfs-openfolder', $source, '-ov', - $source, '-o', $hybridImage) != 0) { - cleanupDie('hdiutil makehybrid failed'); - } - - $uncompressedImage = $hybridImage; - - # $source is no longer needed and will be removed before anything - # else can fail. splice in this form is the same as pop/push. - splice(@gCleanup, -1, 1, - sub {commandInternalVerbosity(0, 'unlink', $hybridImage);}); - - if(command($gConfig{'cmd_rm'}, '-rf', $source) != 0) { - cleanupDie('rm -rf failed'); - } - - if(!$gConfig{'partition_table'} && $gConfig{'recursive_access'}) { - # Even if we do want to create disk images without partition tables, - # it's impossible unless recursive_access is set. - my($rootDevice, $partitionDevice, $partitionMountPoint); - - if(!(($rootDevice, $partitionDevice, $partitionMountPoint) = - hdidMountImage($tempMount, '-readonly', $hybridImage))) { - cleanupDie('hdid mount failed'); - } - - push(@gCleanup, sub {commandVerbosity(0, - $gConfig{'cmd_diskutil'}, 'eject', $rootDevice);}); - - my($udrwImage); - $udrwImage = giveExtension($tempDir.'/udrw', '.dmg'); - - if(command($gConfig{'cmd_hdiutil'}, 'create', '-format', 'UDRW', - '-ov', '-srcdevice', $partitionDevice, $udrwImage) != 0) { - cleanupDie('hdiutil create failed'); - } - - $uncompressedImage = $udrwImage; - - # Going to eject before anything else can fail. Get the eject off - # the stack. - pop(@gCleanup); - - # $hybridImage will be removed soon, but until then, it needs to - # stay on the cleanup stack. It needs to wait until after - # ejection. $udrwImage is staying around. Make it appear as - # though it's been done before $hybridImage. - # - # splice in this form is the same as popping one element to - # @tempCleanup and pushing the subroutine. - my(@tempCleanup); - @tempCleanup = splice(@gCleanup, -1, 1, - sub {commandInternalVerbosity(0, 'unlink', $udrwImage);}); - push(@gCleanup, @tempCleanup); - - if(command($gConfig{'cmd_diskutil'}, 'eject', $rootDevice) != 0) { - cleanupDie('diskutil eject failed'); - } - - # Pop unlink of $uncompressedImage - pop(@gCleanup); - - if(commandInternal('unlink', $hybridImage) != 1) { - cleanupDie('unlink hybridImage failed: '.$!); - } - } - } - else { - # makehybrid is not available, fall back to making a UDRW and - # converting to a compressed image. It ought to be possible to - # create a compressed image directly, but those come out far too - # large (journaling?) and need to be read-write to fix up the - # volume icon anyway. Luckily, we can take advantage of a single - # call back into this function. - my($udrwImage); - $udrwImage = giveExtension($tempDir.'/udrw', '.dmg'); - - diskImageMaker($source, $udrwImage, 'UDRW', $name, $tempDir, - $tempMount, $baseName, $setRootIcon); - - # The call back into diskImageMaker already removed $source. - - $uncompressedImage = $udrwImage; - } - - # The uncompressed disk image is now in its final form. Compress it. - # Jaguar doesn't support hdiutil convert -ov, but it always allows - # overwriting. - # bzip2-compressed UDBZ images can only be created and mounted on 10.4 - # and later. The bzip2-level imagekey is only effective when creating - # images in 10.5. In 10.4, bzip2-level is harmlessly ignored, and the - # default value of 1 is always used. - if(command($gConfig{'cmd_hdiutil'}, 'convert', '-format', $format, - '-imagekey', ($format eq 'UDBZ' ? 'bzip2-level=9' : 'zlib-level=9'), - (defined($gDarwinMajor) && $gDarwinMajor <= 6 ? () : ('-ov')), - $uncompressedImage, '-o', $destination) != 0) { - cleanupDie('hdiutil convert failed'); - } - - # $uncompressedImage is going to be unlinked before anything else can - # fail. splice in this form is the same as pop/push. - splice(@gCleanup, -1, 1, - sub {commandInternalVerbosity(0, 'unlink', $destination);}); - - if(commandInternal('unlink', $uncompressedImage) != 1) { - cleanupDie('unlink uncompressedImage failed: '.$!); - } - - # At this point, the only thing that the compressed block has added to - # the cleanup stack is the removal of $destination. $source has already - # been removed, and its cleanup entry has been removed as well. - } - elsif($format eq 'UDRW' || $format eq 'UDSP') { - my(@extraArguments); - if(!$gConfig{'partition_table'}) { - @extraArguments = ('-layout', 'NONE'); - } - - if($gConfig{'create_directly'}) { - # Use -fs HFS+ to suppress the journal. - if(command($gConfig{'cmd_hdiutil'}, 'create', '-format', $format, - @extraArguments, '-fs', 'HFS+', '-volname', $name, - '-ov', '-srcfolder', $source, $destination) != 0) { - cleanupDie('hdiutil create failed'); - } - - # $source is no longer needed and will be removed before anything - # else can fail. splice in this form is the same as pop/push. - splice(@gCleanup, -1, 1, - sub {commandInternalVerbosity(0, 'unlink', $destination);}); - - if(command($gConfig{'cmd_rm'}, '-rf', $source) != 0) { - cleanupDie('rm -rf failed'); - } - } - else { - # hdiutil create does not support -srcfolder or -srcdevice, it only - # knows how to create blank images. Figure out how large an image - # is needed, create it, and fill it. This is needed for Jaguar. - - # Use native block size for hdiutil create -sectors. - delete $ENV{'BLOCKSIZE'}; - - my(@duOutput, $ignore, $sizeBlocks, $sizeOverhead, $sizeTotal, $type); - if(!(@output = commandOutput($gConfig{'cmd_du'}, '-s', $tempRoot)) || - $? != 0) { - cleanupDie('du failed'); - } - ($sizeBlocks, $ignore) = split(' ', $output[0], 2); - - # The filesystem itself takes up 152 blocks of its own blocks for the - # filesystem up to 8192 blocks, plus 64 blocks for every additional - # 4096 blocks or portion thereof. - $sizeOverhead = 152 + 64 * POSIX::ceil( - (($sizeBlocks - 8192) > 0) ? (($sizeBlocks - 8192) / (4096 - 64)) : 0); - - # The number of blocks must be divisible by 8. - my($mod); - if($mod = ($sizeOverhead % 8)) { - $sizeOverhead += 8 - $mod; - } - - # sectors is taken as the size of a disk, not a filesystem, so the - # partition table eats into it. - if($gConfig{'partition_table'}) { - $sizeOverhead += 80; - } - - # That was hard. Leave some breathing room anyway. Use 1024 sectors - # (512kB). These read-write images wouldn't be useful if they didn't - # have at least a little free space. - $sizeTotal = $sizeBlocks + $sizeOverhead + 1024; - - # Minimum sizes - these numbers are larger on Jaguar than on later - # systems. Just use the Jaguar numbers, since it's unlikely to wind - # up here on any other release. - if($gConfig{'partition_table'} && $sizeTotal < 8272) { - $sizeTotal = 8272; - } - if(!$gConfig{'partition_table'} && $sizeTotal < 8192) { - $sizeTotal = 8192; - } - - # hdiutil create without -srcfolder or -srcdevice will not accept - # -format. It uses -type. Fortunately, the two supported formats - # here map directly to the only two supported types. - if ($format eq 'UDSP') { - $type = 'SPARSE'; - } - else { - $type = 'UDIF'; - } - - if(command($gConfig{'cmd_hdiutil'}, 'create', '-type', $type, - @extraArguments, '-fs', 'HFS+', '-volname', $name, - '-ov', '-sectors', $sizeTotal, $destination) != 0) { - cleanupDie('hdiutil create failed'); - } - - push(@gCleanup, - sub {commandInternalVerbosity(0, 'unlink', $destination);}); - - # The rsync will occur shortly. - } - - my($mounted, $rootDevice, $partitionDevice, $partitionMountPoint); - - $mounted=0; - if(!$gConfig{'create_directly'} || $gConfig{'openfolder_bless'} || - $setRootIcon) { - # The disk image only needs to be mounted if: - # create_directly is false, because the content needs to be copied - # openfolder_bless is true, because bless -openfolder needs to run - # setRootIcon is true, because the root needs its attributes set. - if(!(($rootDevice, $partitionDevice, $partitionMountPoint) = - hdidMountImage($tempMount, $destination))) { - cleanupDie('hdid mount failed'); - } - - $mounted=1; - - push(@gCleanup, sub {commandVerbosity(0, - $gConfig{'cmd_diskutil'}, 'eject', $rootDevice);}); - } - - if(!$gConfig{'create_directly'}) { - # Couldn't create and copy directly in one fell swoop. Now that - # the volume is mounted, copy the files. --copy-unsafe-links is - # unnecessary since it was used to copy everything to the staging - # area. There can be no more unsafe links. - if(command($gConfig{'cmd_rsync'}, '-a', - $source.'/',$partitionMountPoint) != 0) { - cleanupDie('rsync to new volume failed'); - } - - # We need to get the rm -rf of $source off the stack, because it's - # being cleaned up here. There are two items now on top of it: - # removing the target image and, above that, ejecting it. Splice it - # out. - my(@tempCleanup); - @tempCleanup = splice(@gCleanup, -2); - # The next splice is the same as popping once and pushing @tempCleanup. - splice(@gCleanup, -1, 1, @tempCleanup); - - if(command($gConfig{'cmd_rm'}, '-rf', $source) != 0) { - cleanupDie('rm -rf failed'); - } - } - - if($gConfig{'openfolder_bless'}) { - # On Tiger, the bless docs say to use --openfolder, but only - # --openfolder is accepted on Panther. Tiger takes it with a single - # dash too. Jaguar is out of luck. - if(command($gConfig{'cmd_bless'}, '-openfolder', - $partitionMountPoint) != 0) { - cleanupDie('bless failed'); - } - } - - setAttributes($partitionMountPoint, @attributes); - - if($setRootIcon) { - # When "hdiutil create -srcfolder" is used, the root folder's - # attributes are not copied to the new volume. Fix up. - - if(command($gConfig{'cmd_SetFile'}, '-a', 'C', - $partitionMountPoint) != 0) { - cleanupDie('SetFile failed'); - } - } - - if($mounted) { - # Pop diskutil eject - pop(@gCleanup); - - if(command($gConfig{'cmd_diskutil'}, 'eject', $rootDevice) != 0) { - cleanupDie('diskutil eject failed'); - } - } - - # End of UDRW/UDSP section. At this point, $source has been removed - # and its cleanup entry has been removed from the stack. - } - else { - cleanupDie('unrecognized format'); - print STDERR ($0.": unrecognized format\n"); - exit(1); - } -} - -# giveExtension($file, $extension) -# -# If $file does not end in $extension, $extension is added. The new -# filename is returned. -sub giveExtension($$) { - my($extension, $file); - ($file, $extension) = @_; - if(substr($file, -length($extension)) ne $extension) { - return $file.$extension; - } - return $file; -} - -# hdidMountImage($mountPoint, @arguments) -# -# Runs the hdid command with arguments specified by @arguments. -# @arguments may be a single-element array containing the name of the -# disk image to mount. Returns a three-element array, with elements -# corresponding to: -# - The root device of the mounted image, suitable for ejection -# - The device corresponding to the mounted partition -# - The mounted partition's mount point -# -# If running on a system that supports easy mounting at points outside -# of the default /Volumes with hdiutil attach, it is used instead of hdid, -# and $mountPoint is used as the mount point. -# -# The root device will differ from the partition device when the disk -# image contains a partition table, otherwise, they will be identical. -# -# If hdid fails, undef is returned. -sub hdidMountImage($@) { - my(@arguments, @command, $mountPoint); - ($mountPoint, @arguments) = @_; - my(@output); - - if($gConfig{'hdiutil_mountpoint'}) { - @command=($gConfig{'cmd_hdiutil'}, 'attach', @arguments, - '-mountpoint', $mountPoint); - } - else { - @command=($gConfig{'cmd_hdid'}, @arguments); - } - - if(!(@output = commandOutput(@command)) || - $? != 0) { - return undef; - } - - if($gDryRun) { - return('/dev/diskX','/dev/diskXsY','/Volumes/'.$volumeName); - } - - my($line, $restOfLine, $rootDevice); - - foreach $line (@output) { - my($device, $mountpoint); - if($line !~ /^\/dev\//) { - # Consider only lines that correspond to /dev entries - next; - } - ($device, $restOfLine) = split(' ', $line, 2); - - if(!defined($rootDevice) || $rootDevice eq '') { - # If this is the first device seen, it's the root device to be - # used for ejection. Keep it. - $rootDevice = $device; - } - - if($restOfLine =~ /(\/.*)/) { - # The first partition with a mount point is the interesting one. It's - # usually Apple_HFS and usually the last one in the list, but beware of - # the possibility of other filesystem types and the Apple_Free partition. - # If the disk image contains no partition table, the partition will not - # have a type, so look for the mount point by looking for a slash. - $mountpoint = $1; - return($rootDevice, $device, $mountpoint); - } - } - - # No mount point? This is bad. If there's a root device, eject it. - if(defined($rootDevice) && $rootDevice ne '') { - # Failing anyway, so don't care about failure - commandVerbosity(0, $gConfig{'cmd_diskutil'}, 'eject', $rootDevice); - } - - return undef; -} - -# isFormatCompressed($format) -# -# Returns true if $format corresponds to a compressed disk image format. -# Returns false otherwise. -sub isFormatCompressed($) { - my($format); - ($format) = @_; - return $format eq 'UDZO' || $format eq 'UDBZ'; -} - -# licenseMaker($text, $resource) -# -# Takes a plain text file at path $text and creates a license agreement -# resource containing the text at path $license. English-only, and -# no special formatting. This is the bare-bones stuff. For more -# intricate license agreements, create your own resource. -# -# ftp://ftp.apple.com/developer/Development_Kits/SLAs_for_UDIFs_1.0.dmg -sub licenseMaker($$) { - my($resource, $text); - ($text, $resource) = @_; - if(!sysopen(*TEXT, $text, O_RDONLY)) { - print STDERR ($0.': licenseMaker: sysopen text: '.$!."\n"); - return 0; - } - if(!sysopen(*RESOURCE, $resource, O_WRONLY|O_CREAT|O_EXCL)) { - print STDERR ($0.': licenseMaker: sysopen resource: '.$!."\n"); - return 0; - } - print RESOURCE << '__EOT__'; -// See /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Script.h for language IDs. -data 'LPic' (5000) { - // Default language ID, 0 = English - $"0000" - // Number of entries in list - $"0001" - - // Entry 1 - // Language ID, 0 = English - $"0000" - // Resource ID, 0 = STR#/TEXT/styl 5000 - $"0000" - // Multibyte language, 0 = no - $"0000" -}; - -resource 'STR#' (5000, "English") { - { - // Language (unused?) = English - "English", - // Agree - "Agree", - // Disagree - "Disagree", -__EOT__ - # This stuff needs double-quotes for interpolations to work. - print RESOURCE (" // Print, ellipsis is 0xC9\n"); - print RESOURCE (" \"Print\xc9\",\n"); - print RESOURCE (" // Save As, ellipsis is 0xC9\n"); - print RESOURCE (" \"Save As\xc9\",\n"); - print RESOURCE (' // Descriptive text, curly quotes are 0xD2 and 0xD3'. - "\n"); - print RESOURCE (' "If you agree to the terms of this license '. - "agreement, click \xd2Agree\xd3 to access the software. If you ". - "do not agree, press \xd2Disagree.\xd3\"\n"); -print RESOURCE << '__EOT__'; - }; -}; - -// Beware of 1024(?) byte (character?) line length limitation. Split up long -// lines. -// If straight quotes are used ("), remember to escape them (\"). -// Newline is \n, to leave a blank line, use two of them. -// 0xD2 and 0xD3 are curly double-quotes ("), 0xD4 and 0xD5 are curly -// single quotes ('), 0xD5 is also the apostrophe. -data 'TEXT' (5000, "English") { -__EOT__ - - while(!eof(*TEXT)) { - my($line); - chop($line = ); - - while(defined($line)) { - my($chunk); - - # Rez doesn't care for lines longer than (1024?) characters. Split - # at less than half of that limit, in case everything needs to be - # backwhacked. - if(length($line)>500) { - $chunk = substr($line, 0, 500); - $line = substr($line, 500); - } - else { - $chunk = $line; - $line = undef; - } - - if(length($chunk) > 0) { - # Unsafe characters are the double-quote (") and backslash (\), escape - # them with backslashes. - $chunk =~ s/(["\\])/\\$1/g; - - print RESOURCE ' "'.$chunk.'"'."\n"; - } - } - print RESOURCE ' "\n"'."\n"; - } - close(*TEXT); - - print RESOURCE << '__EOT__'; -}; - -data 'styl' (5000, "English") { - // Number of styles following = 1 - $"0001" - - // Style 1. This is used to display the first two lines in bold text. - // Start character = 0 - $"0000 0000" - // Height = 16 - $"0010" - // Ascent = 12 - $"000C" - // Font family = 1024 (Lucida Grande) - $"0400" - // Style bitfield, 0x1=bold 0x2=italic 0x4=underline 0x8=outline - // 0x10=shadow 0x20=condensed 0x40=extended - $"00" - // Style, unused? - $"02" - // Size = 12 point - $"000C" - // Color, RGB - $"0000 0000 0000" -}; -__EOT__ - close(*RESOURCE); - - return 1; -} - -# pathSplit($pathname) -# -# Splits $pathname into an array of path components. -sub pathSplit($) { - my($pathname); - ($pathname) = @_; - return split(/\//, $pathname); -} - -# setAttributes($root, @attributeList) -# -# @attributeList is an array, each element of which must be in the form -# :. is a list of attributes, per SetFile. is a file -# which is taken as relative to $root (even if it appears as an absolute -# path.) SetFile is called to set the attributes on each file in -# @attributeList. -sub setAttributes($@) { - my(@attributes, $root); - ($root, @attributes) = @_; - my($attribute); - foreach $attribute (@attributes) { - my($attrList, $file, @fileList, @fixedFileList); - ($attrList, @fileList) = split(/:/, $attribute); - if(!defined($attrList) || !@fileList) { - cleanupDie('--attribute requires :'); - } - @fixedFileList=(); - foreach $file (@fileList) { - if($file =~ /^\//) { - push(@fixedFileList, $root.$file); - } - else { - push(@fixedFileList, $root.'/'.$file); - } - } - if(command($gConfig{'cmd_SetFile'}, '-a', $attrList, @fixedFileList)) { - cleanupDie('SetFile failed to set attributes'); - } - } - return; -} - -sub trapSignal($) { - my($signalName); - ($signalName) = @_; - cleanupDie('exiting on SIG'.$signalName); -} - -sub usage() { - print STDERR ( -"usage: pkg-dmg --source \n". -" --target \n". -" [--format ] (default: UDZO)\n". -" [--volname ] (default: same name as source)\n". -" [--tempdir ] (default: same dir as target)\n". -" [--mkdir ] (make directory in image)\n". -" [--copy [:]] (extra files to add)\n". -" [--symlink [:]] (extra symlinks to add)\n". -" [--license ] (plain text license agreement)\n". -" [--resource ] (flat .r files to merge)\n". -" [--icon ] (volume icon)\n". -" [--attribute :] (set file attributes)\n". -" [--idme] (make Internet-enabled image)\n". -" [--sourcefile] (treat --source as a file)\n". -" [--verbosity ] (0, 1, 2; default=2)\n". -" [--dry-run] (print what would be done)\n"); - return; -} diff --git a/build/unix/mozconfig.linux b/build/unix/mozconfig.linux index 4998081e44..1f124d97a6 100644 --- a/build/unix/mozconfig.linux +++ b/build/unix/mozconfig.linux @@ -1,6 +1,8 @@ if [ "x$IS_NIGHTLY" = "xyes" ]; then - MOZ_AUTOMATION_UPLOAD_SYMBOLS=1 - MOZ_AUTOMATION_UPDATE_PACKAGING=1 + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} fi . "$topsrcdir/build/mozconfig.common" diff --git a/config/config.mk b/config/config.mk index 93ea65e467..1a92eb357f 100644 --- a/config/config.mk +++ b/config/config.mk @@ -99,9 +99,6 @@ endif RM = rm -f -# LIBXUL_DIST is not defined under js/src, thus we make it mean DIST there. -LIBXUL_DIST ?= $(DIST) - # FINAL_TARGET specifies the location into which we copy end-user-shipped # build products (typelibs, components, chrome). It may already be specified by # a moz.build file. @@ -599,7 +596,7 @@ EN_US_OR_L10N_FILE = $(firstword \ EN_US_OR_L10N_FILES = $(foreach f,$(1),$(call EN_US_OR_L10N_FILE,$(f))) ifneq (WINNT,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh +RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh endif # ! WINNT # diff --git a/configure.in b/configure.in index 2905c078d1..ad2a8a83aa 100644 --- a/configure.in +++ b/configure.in @@ -68,6 +68,7 @@ CAIRO_VERSION=1.10 PANGO_VERSION=1.22.0 GTK2_VERSION=2.18.0 GTK3_VERSION=3.4.0 +GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_4 WINDRES_VERSION=2.14.90 W32API_VERSION=3.14 GNOMEUI_VERSION=2.2.0 @@ -134,6 +135,7 @@ EOF break fi MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd` +DIST="$MOZ_BUILD_ROOT/dist" MOZ_PYTHON @@ -807,6 +809,9 @@ fi MOZ_PATH_PROG(RPMBUILD, rpmbuild, :) AC_SUBST(RPMBUILD) +MOZ_PATH_PROG(GENISOIMAGE, genisoimage, :) +MOZ_PATH_PROG(DSYMUTIL, dsymutil, llvm-dsymutil :) + if test "$COMPILE_ENVIRONMENT"; then dnl ======================================================== @@ -949,7 +954,7 @@ TARGET_MD_ARCH=unix DIRENT_INO=d_ino MOZ_USER_DIR=".mozilla" -MOZ_FIX_LINK_PATHS='-Wl,-rpath-link,$(LIBXUL_DIST)/bin -Wl,-rpath-link,$(prefix)/lib' +MOZ_FIX_LINK_PATHS="-Wl,-rpath-link,${DIST}/bin -Wl,-rpath-link,${prefix}/lib" MOZ_FS_LAYOUT=unix @@ -1982,7 +1987,7 @@ case "$target" in fi LDFLAGS=$_SAVE_LDFLAGS - MOZ_FIX_LINK_PATHS='-Wl,-executable_path,$(LIBXUL_DIST)/bin' + MOZ_FIX_LINK_PATHS="-Wl,-executable_path,${DIST}/bin" ;; ia64*-hpux*) @@ -2010,13 +2015,13 @@ ia64*-hpux*) DSO_LDOPTS='-b -Wl,+s' DSO_CFLAGS="" DSO_PIC_CFLAGS="+Z" - MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -o $@' - MKCSHLIB='$(LD) -b +s -L$(LIBXUL_DIST)/bin -o $@' + MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_LDOPTS) -L$(DIST)/bin -o $@' + MKCSHLIB='$(LD) -b +s -L$(DIST)/bin -o $@' CXXFLAGS="$CXXFLAGS -Wc,-ansi_for_scope,on" else DSO_LDOPTS='-b -E +s' - MKSHLIB='$(LD) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -L$(LIBXUL_DIST)/lib -o $@' - MKCSHLIB='$(LD) $(DSO_LDOPTS) -L$(LIBXUL_DIST)/bin -L$(LIBXUL_DIST)/lib -o $@' + MKSHLIB='$(LD) $(DSO_LDOPTS) -L$(DIST)/bin -L$(DIST)/lib -o $@' + MKCSHLIB='$(LD) $(DSO_LDOPTS) -L$(DIST)/bin -L$(DIST)/lib -o $@' fi MOZ_POST_PROGRAM_COMMAND='chatr +s enable' AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES) @@ -2332,7 +2337,10 @@ ia64*-hpux*) else DLL_SUFFIX=".so.1.0" fi - MOZ_FIX_LINK_PATHS='-Wl,-rpath-link,$(LIBXUL_DIST)/bin -Wl,-rpath-link,$(prefix)/lib -Wl,-rpath-link,$(if $(X11BASE),$(X11BASE),/usr/X11R6)/lib' + if test -z "$X11BASE"; then + X11BASE=/usr/X11R6 + fi + MOZ_FIX_LINK_PATHS="$MOZ_FIX_LINK_PATHS -Wl,-rpath-link,${X11BASE}/lib" DSO_CFLAGS='' DSO_PIC_CFLAGS='-fPIC' DSO_LDOPTS='-shared -fPIC' @@ -3304,9 +3312,6 @@ MOZ_ARG_WITH_STRING(libxul-sdk, [ --with-libxul-sdk=PFX Use the libXUL SDK at ], AC_MSG_ERROR([--with-libxul-sdk is not supported anymore.])) -LIBXUL_DIST="$MOZ_BUILD_ROOT/dist" -AC_SUBST(LIBXUL_DIST) - MOZ_CONFIG_NSPR() dnl set GRE_MILESTONE @@ -3388,18 +3393,7 @@ fi if test -n "$MOZ_NATIVE_NSS"; then NSS_LIBS="$NSS_LIBS -lcrmf" else - NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss' - - if test -z "$GNU_CC" -a "$OS_ARCH" = "WINNT"; then - NSS_LIBS="\ - \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)crmf.\$(LIB_SUFFIX) \ - \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)smime$NSS_VERSION.\$(LIB_SUFFIX) \ - \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)ssl$NSS_VERSION.\$(LIB_SUFFIX) \ - \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)nss$NSS_VERSION.\$(LIB_SUFFIX) \ - \$(LIBXUL_DIST)/lib/\$(LIB_PREFIX)nssutil$NSS_VERSION.\$(LIB_SUFFIX)" - else - NSS_LIBS='-L$(LIBXUL_DIST)/lib'" -lcrmf -lsmime$NSS_VERSION -lssl$NSS_VERSION -lnss$NSS_VERSION -lnssutil$NSS_VERSION" - fi + NSS_CFLAGS="-I${DIST}/include/nss" fi dnl ====================== @@ -3733,6 +3727,7 @@ MOZ_ANDROID_SEARCH_ACTIVITY= MOZ_ANDROID_DOWNLOADS_INTEGRATION= MOZ_ANDROID_MLS_STUMBLER= MOZ_ANDROID_SHARE_OVERLAY= +MOZ_EXCLUDE_HYPHENATION_DICTIONARIES= ACCESSIBILITY= MOZ_TIME_MANAGER= MOZ_SIMPLEPUSH= @@ -4148,6 +4143,8 @@ if test "$COMPILE_ENVIRONMENT"; then dnl of a make reference because of how TK_LIBS is mangled in toolkit/library/moz.build dnl for GTK+3 builds. TK_LIBS=$MOZ_GTK3_LIBS + AC_DEFINE_UNQUOTED(GDK_VERSION_MIN_REQUIRED,$GDK_VERSION_MIN_REQUIRED) + AC_DEFINE_UNQUOTED(GDK_VERSION_MAX_ALLOWED,$GDK_VERSION_MIN_REQUIRED) GLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32 fi if test "$MOZ_ENABLE_GTK2"; then @@ -4780,6 +4777,13 @@ if test -n "$MOZ_ANDROID_TAB_QUEUE"; then AC_DEFINE(MOZ_ANDROID_TAB_QUEUE) fi +dnl ========================================================= +dnl = Whether to exclude hyphenations files in the build +dnl ========================================================= +if test -n "$MOZ_EXCLUDE_HYPHENATION_DICTIONARIES"; then + AC_DEFINE(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES) +fi + dnl ======================================================== dnl = Enable IPDL's "expensive" unit tests dnl ======================================================== @@ -5838,6 +5842,74 @@ fi AC_SUBST(MOZ_GAMEPAD) AC_SUBST(MOZ_GAMEPAD_BACKEND) +dnl ======================================================== +dnl = Breakpad crash reporting (on by default on supported platforms) +dnl ======================================================== + +case $target in +i?86-*-mingw*|x86_64-*-mingw*) + MOZ_CRASHREPORTER=1 + ;; +i?86-apple-darwin*|x86_64-apple-darwin*) + if test -z "$MOZ_IOS"; then + MOZ_CRASHREPORTER=1 + fi + ;; +i?86-*-linux*|x86_64-*-linux*|arm-*-linux*) + if test "$MOZ_ENABLE_GTK"; then + MOZ_CRASHREPORTER=1 + fi + ;; +*-android*|*-linuxandroid*) + MOZ_CRASHREPORTER=1 + ;; +*solaris*) + MOZ_CRASHREPORTER=1 + ;; +esac + +dnl = disabled anyway on AF +MOZ_CRASHREPORTER= + +MOZ_ARG_DISABLE_BOOL(crashreporter, +[ --disable-crashreporter Disable breakpad crash reporting], + [MOZ_CRASHREPORTER=], + [MOZ_CRASHREPORTER=F # Force enable breakpad]) + +if test "$OS_ARCH" != "$HOST_OS_ARCH" -a "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "Darwin"; then + if test "$MOZ_CRASHREPORTER" = F; then + AC_MSG_ERROR([Cannot --enable-crashreporter, as breakpad tools do not support compiling on $HOST_OS_ARCH while targeting $OS_ARCH.]) + fi + MOZ_CRASHREPORTER= +fi + +if test -n "$MOZ_CRASHREPORTER"; then + AC_DEFINE(MOZ_CRASHREPORTER) + + if test "$OS_TARGET" = "Linux" -o "$OS_ARCH" = "SunOS" && \ + test -z "$SKIP_LIBRARY_CHECKS"; then + PKG_CHECK_MODULES(MOZ_GTHREAD, gthread-2.0) + fi + + if test "$OS_ARCH" = "WINNT"; then + if test -z "$HAVE_64BIT_BUILD"; then + MOZ_CRASHREPORTER_INJECTOR=1 + AC_DEFINE(MOZ_CRASHREPORTER_INJECTOR) + fi + fi +fi + +MOZ_ARG_WITH_STRING(crashreporter-enable-percent, +[ --with-crashreporter-enable-percent=NN + Enable sending crash reports by default on NN% of users. (default=100)], +[ val=`echo $withval | sed 's/[^0-9]//g'` + MOZ_CRASHREPORTER_ENABLE_PERCENT="$val"]) + +if test -z "$MOZ_CRASHREPORTER_ENABLE_PERCENT"; then + MOZ_CRASHREPORTER_ENABLE_PERCENT=100 +fi +AC_DEFINE_UNQUOTED(MOZ_CRASHREPORTER_ENABLE_PERCENT, $MOZ_CRASHREPORTER_ENABLE_PERCENT) + dnl ======================================================== dnl = libjpeg-turbo configuration dnl ======================================================== @@ -7799,7 +7871,7 @@ fi AC_SUBST(HAVE_INTTYPES_H) if test "$MOZ_TREE_CAIRO"; then - MOZ_CAIRO_CFLAGS='-I$(LIBXUL_DIST)/include/cairo' + MOZ_CAIRO_CFLAGS="-I${DIST}/include/cairo" AC_DEFINE(MOZ_TREE_CAIRO) if test "$OS_ARCH" = "WINNT"; then @@ -8202,6 +8274,7 @@ AC_SUBST(MOZ_ANDROID_SHARE_OVERLAY) AC_SUBST(MOZ_ANDROID_TAB_QUEUE) AC_SUBST(MOZ_ANDROID_MLS_STUMBLER) AC_SUBST(MOZ_ANDROID_DOWNLOADS_INTEGRATION) +AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES) AC_SUBST(ENABLE_STRIP) AC_SUBST(PKG_SKIP_STRIP) AC_SUBST(STRIP_FLAGS) @@ -8557,6 +8630,8 @@ AC_SUBST(MOZ_SZIP_FLAGS) AC_DEFINE(MOZ_PHOENIX_EXTENSIONS) AC_SUBST(MOZ_PHOENIX_EXTENSIONS) +AC_SUBST(DMG_TOOL) + dnl Host JavaScript runtime, if any, to use during cross compiles. AC_SUBST(JS_BINARY) diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index 4296b80234..94a493a97f 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -7699,7 +7699,7 @@ nsContentUtils::ToWidgetPoint(const CSSPoint& aPoint, nsPresContext* aPresContext) { return LayoutDeviceIntPoint::FromAppUnitsRounded( - CSSPoint::ToAppUnits(aPoint) + aOffset, + (CSSPoint::ToAppUnits(aPoint) + aOffset).ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()), aPresContext->AppUnitsPerDevPixel()); } diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index d308b954d6..ba294aa01b 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -488,11 +488,7 @@ nsDOMWindowUtils::SetResolution(float aResolution) return NS_ERROR_FAILURE; } - nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); - if (sf) { - sf->SetResolution(aResolution); - presShell->SetResolution(aResolution); - } + presShell->SetResolution(aResolution); return NS_OK; } @@ -536,8 +532,7 @@ nsDOMWindowUtils::GetIsResolutionSet(bool* aIsResolutionSet) { return NS_ERROR_FAILURE; } - const nsIScrollableFrame* sf = presShell->GetRootScrollFrameAsScrollable(); - *aIsResolutionSet = sf && sf->IsResolutionSet(); + *aIsResolutionSet = presShell->IsResolutionSet(); return NS_OK; } @@ -1023,7 +1018,7 @@ nsDOMWindowUtils::SendKeyEvent(const nsAString& aType, // get the widget to send the event to nsCOMPtr widget = GetWidget(); - + return nsContentUtils::SendKeyEvent(widget, aType, aKeyCode, aCharCode, aModifiers, aAdditionalFlags, aDefaultActionTaken); @@ -1379,7 +1374,7 @@ nsDOMWindowUtils::NodesFromRect(float aX, float aY, nsCOMPtr doc = GetDocument(); NS_ENSURE_STATE(doc); - return doc->NodesFromRectHelper(aX, aY, aTopSize, aRightSize, aBottomSize, aLeftSize, + return doc->NodesFromRectHelper(aX, aY, aTopSize, aRightSize, aBottomSize, aLeftSize, aIgnoreRootScrollFrame, aFlushLayout, aReturn); } @@ -1477,7 +1472,9 @@ CanvasToDataSourceSurface(nsIDOMHTMLCanvasElement* aCanvas) "be an element."); nsLayoutUtils::SurfaceFromElementResult result = nsLayoutUtils::SurfaceFromElement(node->AsElement()); - return result.mSourceSurface->GetDataSurface(); + + MOZ_ASSERT(result.GetSourceSurface()); + return result.GetSourceSurface()->GetDataSurface(); } NS_IMETHODIMP @@ -1846,7 +1843,7 @@ nsDOMWindowUtils::GetFullZoom(float* aFullZoom) return NS_OK; } - + NS_IMETHODIMP nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget, nsIDOMEvent* aEvent, @@ -2590,7 +2587,7 @@ nsDOMWindowUtils::RenderDocument(const nsRect& aRect, return presShell->RenderDocument(aRect, aFlags, aBackgroundColor, aThebesContext); } -NS_IMETHODIMP +NS_IMETHODIMP nsDOMWindowUtils::GetCursorType(int16_t *aCursor) { MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 3563d21626..6aada8fd7e 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -1203,8 +1203,8 @@ bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode) gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->MakeCurrent(); gfxPlatform::GetPlatform()->GetSkiaGLGlue()->GetGLContext()->fDeleteTextures(1, &mVideoTexture); } - mCurrentVideoSize.width = 0; - mCurrentVideoSize.height = 0; + mCurrentVideoSize.width = 0; + mCurrentVideoSize.height = 0; } #endif @@ -2107,15 +2107,14 @@ CanvasRenderingContext2D::CreatePattern(const CanvasImageSource& source, nsLayoutUtils::SurfaceFromElement(htmlElement, nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget); - if (!res.mSourceSurface) { + if (!res.GetSourceSurface()) { error.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } - RefPtr pat = - new CanvasPattern(this, res.mSourceSurface, repeatMode, res.mPrincipal, - res.mIsWriteOnly, res.mCORSUsed); - + RefPtr pat = new CanvasPattern(this, res.GetSourceSurface(), repeatMode, + res.mPrincipal, res.mIsWriteOnly, + res.mCORSUsed); return pat.forget(); } @@ -5412,9 +5411,9 @@ CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w uint8_t* srcLine = aArray->Data() + copyY * (w * 4) + copyX * 4; #if 0 printf("PutImageData_explicit: dirty x=%d y=%d w=%d h=%d copy x=%d y=%d w=%d h=%d ext x=%d y=%d w=%d h=%d\n", - dirtyRect.x, dirtyRect.y, copyWidth, copyHeight, - copyX, copyY, copyWidth, copyHeight, - x, y, w, h); + dirtyRect.x, dirtyRect.y, copyWidth, copyHeight, + copyX, copyY, copyWidth, copyHeight, + x, y, w, h); #endif for (uint32_t j = 0; j < copyHeight; j++) { uint8_t *src = srcLine; diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp index 8438e7106f..7277871534 100644 --- a/dom/canvas/ImageBitmap.cpp +++ b/dom/canvas/ImageBitmap.cpp @@ -290,12 +290,13 @@ GetSurfaceFromElement(nsIGlobalObject* aGlobal, HTMLElementType& aElement, Error return nullptr; } - if (NS_WARN_IF(!res.mSourceSurface)) { + RefPtr surface = res.GetSourceSurface(); + + if (NS_WARN_IF(!surface)) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } - RefPtr surface(res.mSourceSurface); return surface.forget(); } diff --git a/dom/canvas/TexUnpackBlob.cpp b/dom/canvas/TexUnpackBlob.cpp new file mode 100644 index 0000000000..a4f57b0e90 --- /dev/null +++ b/dom/canvas/TexUnpackBlob.cpp @@ -0,0 +1,773 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TexUnpackBlob.h" + +#include "GLBlitHelper.h" +#include "GLContext.h" +#include "GLDefs.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/HTMLCanvasElement.h" +#include "mozilla/RefPtr.h" +#include "nsLayoutUtils.h" +#include "WebGLContext.h" +#include "WebGLTexelConversions.h" +#include "WebGLTexture.h" + +namespace mozilla { +namespace webgl { + +static GLenum +DoTexOrSubImage(bool isSubImage, gl::GLContext* gl, TexImageTarget target, GLint level, + const DriverUnpackInfo* dui, GLint xOffset, GLint yOffset, GLint zOffset, + GLsizei width, GLsizei height, GLsizei depth, const void* data) +{ + if (isSubImage) { + return DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height, + depth, dui->ToPacking(), data); + } else { + return DoTexImage(gl, target, level, dui, width, height, depth, data); + } +} + +/*static*/ void +TexUnpackBlob::OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src, + gl::OriginPos* const out_dst) +{ + // Our surfaces are TopLeft. + *out_src = gl::OriginPos::TopLeft; + + // WebGL specs the default as passing DOM elements top-left first. + // Thus y-flip would give us bottom-left. + *out_dst = webgl->mPixelStore_FlipY ? gl::OriginPos::BottomLeft + : gl::OriginPos::TopLeft; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// TexUnpackBytes + +bool +TexUnpackBytes::ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, + const webgl::PackingInfo& pi) +{ + if (!mBytes) + return true; + + const auto bytesPerPixel = webgl::BytesPerPixel(pi); + const auto bytesNeeded = webgl->GetUnpackSize(isFunc3D, mWidth, mHeight, mDepth, + bytesPerPixel); + if (!bytesNeeded.isValid()) { + webgl->ErrorInvalidOperation("%s: Overflow while computing the needed buffer" + " size.", + funcName); + return false; + } + + if (mByteCount < bytesNeeded.value()) { + webgl->ErrorInvalidOperation("%s: Provided buffer is too small. (needs %u, has" + " %u)", + funcName, bytesNeeded.value(), mByteCount); + return false; + } + + return true; +} + +static bool +UnpackFormatHasAlpha(GLenum unpackFormat) +{ + switch (unpackFormat) { + case LOCAL_GL_ALPHA: + case LOCAL_GL_LUMINANCE_ALPHA: + case LOCAL_GL_RGBA: + return true; + + default: + return false; + } +} + +static WebGLTexelFormat +FormatFromPacking(const webgl::PackingInfo& pi) +{ + switch (pi.type) { + case LOCAL_GL_UNSIGNED_SHORT_5_6_5: + return WebGLTexelFormat::RGB565; + + case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: + return WebGLTexelFormat::RGBA5551; + + case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: + return WebGLTexelFormat::RGBA4444; + + case LOCAL_GL_UNSIGNED_BYTE: + switch (pi.format) { + case LOCAL_GL_LUMINANCE: return WebGLTexelFormat::R8; + case LOCAL_GL_ALPHA: return WebGLTexelFormat::A8; + case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA8; + case LOCAL_GL_RGB: return WebGLTexelFormat::RGB8; + case LOCAL_GL_SRGB: return WebGLTexelFormat::RGB8; + case LOCAL_GL_RGBA: return WebGLTexelFormat::RGBA8; + case LOCAL_GL_SRGB_ALPHA: return WebGLTexelFormat::RGBA8; + } + + case LOCAL_GL_HALF_FLOAT: + case LOCAL_GL_HALF_FLOAT_OES: + switch (pi.format) { + case LOCAL_GL_LUMINANCE: return WebGLTexelFormat::R16F; + case LOCAL_GL_ALPHA: return WebGLTexelFormat::A16F; + case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA16F; + case LOCAL_GL_RGB: return WebGLTexelFormat::RGB16F; + case LOCAL_GL_RGBA: return WebGLTexelFormat::RGBA16F; + } + + case LOCAL_GL_FLOAT: + switch (pi.format) { + case LOCAL_GL_LUMINANCE: return WebGLTexelFormat::R32F; + case LOCAL_GL_ALPHA: return WebGLTexelFormat::A32F; + case LOCAL_GL_LUMINANCE_ALPHA: return WebGLTexelFormat::RA32F; + case LOCAL_GL_RGB: return WebGLTexelFormat::RGB32F; + case LOCAL_GL_RGBA: return WebGLTexelFormat::RGBA32F; + } + } + + return WebGLTexelFormat::FormatNotSupportingAnyConversion; +} + +void +TexUnpackBytes::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum* const out_glError) +{ + WebGLContext* webgl = tex->mContext; + gl::GLContext* gl = webgl->gl; + + const void* uploadBytes = mBytes; + UniqueBuffer tempBuffer; + + do { + if (!webgl->mPixelStore_FlipY && !webgl->mPixelStore_PremultiplyAlpha) + break; + + if (!mBytes || !mWidth || !mHeight || !mDepth) + break; + + if (webgl->IsWebGL2()) + break; + MOZ_ASSERT(mDepth == 1); + + // This is literally the worst. + webgl->GenerateWarning("%s: Uploading ArrayBuffers with FLIP_Y or" + " PREMULTIPLY_ALPHA is slow.", + funcName); + + tempBuffer = malloc(mByteCount); + if (!tempBuffer) { + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + return; + } + + const webgl::PackingInfo pi = { dui->unpackFormat, dui->unpackType }; + + const auto bytesPerPixel = webgl::BytesPerPixel(pi); + const auto rowByteAlignment = webgl->mPixelStore_UnpackAlignment; + + const size_t bytesPerRow = bytesPerPixel * mWidth; + const size_t rowStride = RoundUpToMultipleOf(bytesPerRow, rowByteAlignment); + + const bool needsYFlip = webgl->mPixelStore_FlipY; + + bool needsAlphaPremult = webgl->mPixelStore_PremultiplyAlpha; + if (!UnpackFormatHasAlpha(pi.format)) + needsAlphaPremult = false; + + if (!needsAlphaPremult) { + if (!webgl->mPixelStore_FlipY) + break; + + const uint8_t* src = (const uint8_t*)mBytes; + const uint8_t* const srcEnd = src + rowStride * mHeight; + + uint8_t* dst = (uint8_t*)tempBuffer.get() + rowStride * (mHeight - 1); + + while (src != srcEnd) { + memcpy(dst, src, bytesPerRow); + src += rowStride; + dst -= rowStride; + } + + uploadBytes = tempBuffer.get(); + break; + } + + const auto texelFormat = FormatFromPacking(pi); + if (texelFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) { + MOZ_ASSERT(false, "Bad texelFormat from pi."); + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + return; + } + + const auto srcOrigin = gl::OriginPos::BottomLeft; + const auto dstOrigin = (needsYFlip ? gl::OriginPos::TopLeft + : gl::OriginPos::BottomLeft); + + const bool srcPremultiplied = false; + const bool dstPremultiplied = needsAlphaPremult; // Always true here. + // And go!: + if (!ConvertImage(mWidth, mHeight, + mBytes, rowStride, srcOrigin, texelFormat, srcPremultiplied, + tempBuffer.get(), rowStride, dstOrigin, texelFormat, + dstPremultiplied)) + { + MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + return; + } + + uploadBytes = tempBuffer.get(); + } while (false); + + GLenum error = DoTexOrSubImage(isSubImage, gl, target, level, dui, xOffset, yOffset, + zOffset, mWidth, mHeight, mDepth, uploadBytes); + *out_glError = error; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// TexUnpackImage + +TexUnpackImage::TexUnpackImage(const RefPtr& image, bool isAlphaPremult) + : TexUnpackBlob(image->GetSize().width, image->GetSize().height, 1, true) + , mImage(image) + , mIsAlphaPremult(isAlphaPremult) +{ } + +TexUnpackImage::~TexUnpackImage() +{ } + +void +TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum* const out_glError) +{ + MOZ_ASSERT_IF(needsRespec, !isSubImage); + *out_glError = 0; + + WebGLContext* webgl = tex->mContext; + + gl::GLContext* gl = webgl->GL(); + gl->MakeCurrent(); + + if (needsRespec) { + GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, dui, xOffset, + yOffset, zOffset, mWidth, mHeight, mDepth, + nullptr); + if (error) { + MOZ_ASSERT(!error); + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + return; + } + } + + do { + if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) + break; + + if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) + break; + + gl::ScopedFramebuffer scopedFB(gl); + gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB()); + + { + gl::GLContext::LocalErrorScope errorScope(*gl); + + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, + target.get(), tex->mGLName, level); + + if (errorScope.GetError()) + break; + } + + const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) + break; + + gl::OriginPos srcOrigin, dstOrigin; + OriginsForDOM(webgl, &srcOrigin, &dstOrigin); + + const gfx::IntSize destSize(mWidth, mHeight); + if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(), + dstOrigin)) + { + break; + } + + return; // Blitting was successful, so we're done! + } while (false); + + TexUnpackSurface surfBlob(mImage->GetAsSourceSurface(), mIsAlphaPremult); + + surfBlob.TexOrSubImage(isSubImage, needsRespec, funcName, tex, target, level, dui, + xOffset, yOffset, zOffset, out_glError); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// TexUnpackSurface + +static bool +GuessAlignment(const void* data, size_t bytesPerRow, size_t stride, size_t maxAlignment, + size_t* const out_alignment) +{ + size_t alignmentGuess = maxAlignment; + while (alignmentGuess) { + size_t guessStride = RoundUpToMultipleOf(bytesPerRow, alignmentGuess); + if (guessStride == stride && + uintptr_t(data) % alignmentGuess == 0) + { + *out_alignment = alignmentGuess; + return true; + } + alignmentGuess /= 2; + } + return false; +} + +static bool +SupportsBGRA(gl::GLContext* gl) +{ + if (gl->IsANGLE()) + return true; + + return false; +} + +/*static*/ bool +TexUnpackSurface::UploadDataSurface(bool isSubImage, WebGLContext* webgl, + TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, + GLsizei height, gfx::DataSourceSurface* surf, + bool isSurfAlphaPremult, GLenum* const out_glError) +{ + gl::GLContext* gl = webgl->GL(); + MOZ_ASSERT(gl->IsCurrent()); + *out_glError = 0; + + if (isSurfAlphaPremult != webgl->mPixelStore_PremultiplyAlpha) + return false; + + gl::OriginPos srcOrigin, dstOrigin; + OriginsForDOM(webgl, &srcOrigin, &dstOrigin); + if (srcOrigin != dstOrigin) + return false; + + // This differs from the raw-data upload in that we choose how we do the unpack. + // (alignment, etc.) + + // Uploading RGBX as RGBA and blitting to RGB is faster than repacking RGBX into + // RGB on the CPU. However, this is optimization is out-of-scope for now. + + static const webgl::DriverUnpackInfo kInfoBGRA = { + LOCAL_GL_BGRA, + LOCAL_GL_BGRA, + LOCAL_GL_UNSIGNED_BYTE, + }; + + const webgl::DriverUnpackInfo* chosenDUI = nullptr; + + switch (surf->GetFormat()) { + case gfx::SurfaceFormat::B8G8R8A8: + if (SupportsBGRA(gl) && + dui->internalFormat == LOCAL_GL_RGBA && + dui->unpackFormat == LOCAL_GL_RGBA && + dui->unpackType == LOCAL_GL_UNSIGNED_BYTE) + { + chosenDUI = &kInfoBGRA; + } + break; + + case gfx::SurfaceFormat::R8G8B8A8: + if (dui->unpackFormat == LOCAL_GL_RGBA && + dui->unpackType == LOCAL_GL_UNSIGNED_BYTE) + { + chosenDUI = dui; + } + break; + + case gfx::SurfaceFormat::R5G6B5_UINT16: + if (dui->unpackFormat == LOCAL_GL_RGB && + dui->unpackType == LOCAL_GL_UNSIGNED_SHORT_5_6_5) + { + chosenDUI = dui; + } + break; + + default: + break; + } + + if (!chosenDUI) + return false; + + gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::MapType::READ); + if (!map.IsMapped()) + return false; + + const webgl::PackingInfo pi = {chosenDUI->unpackFormat, chosenDUI->unpackType}; + const auto bytesPerPixel = webgl::BytesPerPixel(pi); + const size_t bytesPerRow = width * bytesPerPixel; + + const GLint kMaxUnpackAlignment = 8; + size_t unpackAlignment; + if (!GuessAlignment(map.GetData(), bytesPerRow, map.GetStride(), kMaxUnpackAlignment, + &unpackAlignment)) + { + return false; + // TODO: Consider using UNPACK_ settings to set the stride based on the too-large + // alignment used for some SourceSurfaces. (D2D allegedy likes alignment=16) + } + + MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); + ScopedUnpackReset scopedReset(webgl); + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, unpackAlignment); + + const GLsizei depth = 1; + GLenum error = DoTexOrSubImage(isSubImage, gl, target.get(), level, chosenDUI, + xOffset, yOffset, zOffset, width, height, depth, + map.GetData()); + if (error) { + *out_glError = error; + return false; + } + + return true; +} + +//////////////////// + +static bool +GetFormatForSurf(gfx::SourceSurface* surf, WebGLTexelFormat* const out_texelFormat) +{ + gfx::SurfaceFormat surfFormat = surf->GetFormat(); + + switch (surfFormat) { + case gfx::SurfaceFormat::B8G8R8A8: + *out_texelFormat = WebGLTexelFormat::BGRA8; + return true; + + case gfx::SurfaceFormat::B8G8R8X8: + *out_texelFormat = WebGLTexelFormat::BGRX8; + return true; + + case gfx::SurfaceFormat::R8G8B8A8: + *out_texelFormat = WebGLTexelFormat::RGBA8; + return true; + + case gfx::SurfaceFormat::R8G8B8X8: + *out_texelFormat = WebGLTexelFormat::RGBX8; + return true; + + case gfx::SurfaceFormat::R5G6B5_UINT16: + *out_texelFormat = WebGLTexelFormat::RGB565; + return true; + + case gfx::SurfaceFormat::A8: + *out_texelFormat = WebGLTexelFormat::A8; + return true; + + case gfx::SurfaceFormat::YUV: + // Ugh... + NS_ERROR("We don't handle uploads from YUV sources yet."); + // When we want to, check out gfx/ycbcr/YCbCrUtils.h. (specifically + // GetYCbCrToRGBDestFormatAndSize and ConvertYCbCrToRGB) + return false; + + default: + return false; + } +} + +static bool +GetFormatForPackingTuple(GLenum packingFormat, GLenum packingType, + WebGLTexelFormat* const out_texelFormat) +{ + switch (packingType) { + case LOCAL_GL_UNSIGNED_BYTE: + switch (packingFormat) { + case LOCAL_GL_RED: + case LOCAL_GL_LUMINANCE: + *out_texelFormat = WebGLTexelFormat::R8; + return true; + + case LOCAL_GL_ALPHA: + *out_texelFormat = WebGLTexelFormat::A8; + return true; + + case LOCAL_GL_LUMINANCE_ALPHA: + *out_texelFormat = WebGLTexelFormat::RA8; + return true; + + case LOCAL_GL_RGB: + *out_texelFormat = WebGLTexelFormat::RGB8; + return true; + + case LOCAL_GL_RGBA: + *out_texelFormat = WebGLTexelFormat::RGBA8; + return true; + + default: + break; + } + break; + + case LOCAL_GL_UNSIGNED_SHORT_5_6_5: + switch (packingFormat) { + case LOCAL_GL_RGB: + *out_texelFormat = WebGLTexelFormat::RGB565; + return true; + + default: + break; + } + break; + + case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: + switch (packingFormat) { + case LOCAL_GL_RGBA: + *out_texelFormat = WebGLTexelFormat::RGBA5551; + return true; + + default: + break; + } + break; + + case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: + switch (packingFormat) { + case LOCAL_GL_RGBA: + *out_texelFormat = WebGLTexelFormat::RGBA4444; + return true; + + default: + break; + } + break; + + case LOCAL_GL_HALF_FLOAT: + case LOCAL_GL_HALF_FLOAT_OES: + switch (packingFormat) { + case LOCAL_GL_RED: + case LOCAL_GL_LUMINANCE: + *out_texelFormat = WebGLTexelFormat::R16F; + return true; + + case LOCAL_GL_ALPHA: + *out_texelFormat = WebGLTexelFormat::A16F; + return true; + + case LOCAL_GL_LUMINANCE_ALPHA: + *out_texelFormat = WebGLTexelFormat::RA16F; + return true; + + case LOCAL_GL_RGB: + *out_texelFormat = WebGLTexelFormat::RGB16F; + return true; + + case LOCAL_GL_RGBA: + *out_texelFormat = WebGLTexelFormat::RGBA16F; + return true; + + default: + break; + } + break; + + case LOCAL_GL_FLOAT: + switch (packingFormat) { + case LOCAL_GL_RED: + case LOCAL_GL_LUMINANCE: + *out_texelFormat = WebGLTexelFormat::R32F; + return true; + + case LOCAL_GL_ALPHA: + *out_texelFormat = WebGLTexelFormat::A32F; + return true; + + case LOCAL_GL_LUMINANCE_ALPHA: + *out_texelFormat = WebGLTexelFormat::RA32F; + return true; + + case LOCAL_GL_RGB: + *out_texelFormat = WebGLTexelFormat::RGB32F; + return true; + + case LOCAL_GL_RGBA: + *out_texelFormat = WebGLTexelFormat::RGBA32F; + return true; + + default: + break; + } + break; + + default: + break; + } + + NS_ERROR("Unsupported EffectiveFormat dest format for DOM element upload."); + return false; +} + +/*static*/ bool +TexUnpackSurface::ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui, + gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, + UniqueBuffer* const out_convertedBuffer, + uint8_t* const out_convertedAlignment, + bool* const out_outOfMemory) +{ + *out_outOfMemory = false; + + const size_t width = surf->GetSize().width; + const size_t height = surf->GetSize().height; + + // Source args: + + // After we call this, on OSX, our GLContext will no longer be current. + gfx::DataSourceSurface::ScopedMap srcMap(surf, gfx::DataSourceSurface::MapType::READ); + if (!srcMap.IsMapped()) + return false; + + const void* const srcBegin = srcMap.GetData(); + const size_t srcStride = srcMap.GetStride(); + + WebGLTexelFormat srcFormat; + if (!GetFormatForSurf(surf, &srcFormat)) + return false; + + const bool srcPremultiplied = isSurfAlphaPremult; + + // Dest args: + + WebGLTexelFormat dstFormat; + if (!GetFormatForPackingTuple(dui->unpackFormat, dui->unpackType, &dstFormat)) + return false; + + const auto bytesPerPixel = webgl::BytesPerPixel({dui->unpackFormat, dui->unpackType}); + const size_t dstRowBytes = bytesPerPixel * width; + + const size_t dstAlignment = 8; // Just use the max! + const size_t dstStride = RoundUpToMultipleOf(dstRowBytes, dstAlignment); + + CheckedUint32 checkedDstSize = dstStride; + checkedDstSize *= height; + if (!checkedDstSize.isValid()) { + *out_outOfMemory = true; + return false; + } + + const size_t dstSize = checkedDstSize.value(); + + UniqueBuffer dstBuffer = malloc(dstSize); + if (!dstBuffer) { + *out_outOfMemory = true; + return false; + } + void* const dstBegin = dstBuffer.get(); + + gl::OriginPos srcOrigin, dstOrigin; + OriginsForDOM(webgl, &srcOrigin, &dstOrigin); + + const bool dstPremultiplied = webgl->mPixelStore_PremultiplyAlpha; + + // And go!: + if (!ConvertImage(width, height, + srcBegin, srcStride, srcOrigin, srcFormat, srcPremultiplied, + dstBegin, dstStride, dstOrigin, dstFormat, dstPremultiplied)) + { + MOZ_ASSERT(false, "ConvertImage failed unexpectedly."); + NS_ERROR("ConvertImage failed unexpectedly."); + *out_outOfMemory = true; + return false; + } + + *out_convertedBuffer = Move(dstBuffer); + *out_convertedAlignment = dstAlignment; + return true; +} + + +//////////////////// + +TexUnpackSurface::TexUnpackSurface(const RefPtr& surf, + bool isAlphaPremult) + : TexUnpackBlob(surf->GetSize().width, surf->GetSize().height, 1, true) + , mSurf(surf) + , mIsAlphaPremult(isAlphaPremult) +{ } + +TexUnpackSurface::~TexUnpackSurface() +{ } + +void +TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum* const out_glError) +{ + *out_glError = 0; + + WebGLContext* webgl = tex->mContext; + + // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on + // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we + // call into GL, instead of trying to keep MakeCurrent-ed. + + RefPtr dataSurf = mSurf->GetDataSurface(); + MOZ_ASSERT(dataSurf); + + GLenum error; + if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset, + zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error)) + { + return; + } + if (error == LOCAL_GL_OUT_OF_MEMORY) { + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + return; + } + + // CPU conversion. (++numCopies) + + UniqueBuffer convertedBuffer; + uint8_t convertedAlignment; + bool outOfMemory; + if (!ConvertSurface(webgl, dui, dataSurf, mIsAlphaPremult, &convertedBuffer, + &convertedAlignment, &outOfMemory)) + { + if (outOfMemory) { + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + } else { + NS_ERROR("Failed to convert surface."); + *out_glError = LOCAL_GL_OUT_OF_MEMORY; + } + return; + } + + MOZ_ALWAYS_TRUE( webgl->gl->MakeCurrent() ); + ScopedUnpackReset scopedReset(webgl); + webgl->gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, convertedAlignment); + + error = DoTexOrSubImage(isSubImage, webgl->gl, target.get(), level, dui, xOffset, + yOffset, zOffset, mWidth, mHeight, mDepth, + convertedBuffer.get()); + *out_glError = error; +} + +} // namespace webgl +} // namespace mozilla diff --git a/dom/canvas/TexUnpackBlob.h b/dom/canvas/TexUnpackBlob.h new file mode 100644 index 0000000000..ed9589d1bb --- /dev/null +++ b/dom/canvas/TexUnpackBlob.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TEX_UNPACK_BLOB_H_ +#define TEX_UNPACK_BLOB_H_ + +#include "GLContextTypes.h" +#include "GLTypes.h" +#include "WebGLStrongTypes.h" + + +template +class RefPtr; + +namespace mozilla { + +class UniqueBuffer; +class WebGLContext; +class WebGLTexture; + +namespace dom { +class Element; +class HTMLCanvasElement; +class HTMLVideoElement; +} // namespace dom + +namespace gfx { +class DataSourceSurface; +} // namespace gfx + +namespace gl { +class GLContext; +} // namespace gl + +namespace layers { +class Image; +class ImageContainer; +} // namespace layers + +namespace webgl { + +struct PackingInfo; +struct DriverUnpackInfo; + +class TexUnpackBlob +{ +public: + const GLsizei mWidth; + const GLsizei mHeight; + const GLsizei mDepth; + const bool mHasData; + +protected: + TexUnpackBlob(GLsizei width, GLsizei height, GLsizei depth, bool hasData) + : mWidth(width) + , mHeight(height) + , mDepth(depth) + , mHasData(hasData) + { } + +public: + virtual ~TexUnpackBlob() {} + + virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, + const webgl::PackingInfo& pi) = 0; + + virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, + GLenum* const out_glError) = 0; + + static void OriginsForDOM(WebGLContext* webgl, gl::OriginPos* const out_src, + gl::OriginPos* const out_dst); +}; + +class TexUnpackBytes : public TexUnpackBlob +{ +public: + const size_t mByteCount; + const void* const mBytes; + + TexUnpackBytes(GLsizei width, GLsizei height, GLsizei depth, size_t byteCount, + const void* bytes) + : TexUnpackBlob(width, height, depth, bool(bytes)) + , mByteCount(byteCount) + , mBytes(bytes) + { } + + virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, + const webgl::PackingInfo& pi) override; + + virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, + GLenum* const out_glError) override; +}; + +class TexUnpackImage : public TexUnpackBlob +{ +public: + const RefPtr mImage; + const bool mIsAlphaPremult; + + TexUnpackImage(const RefPtr& image, bool isAlphaPremult); + virtual ~TexUnpackImage() override; + + virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, + const webgl::PackingInfo& pi) override + { + return true; + } + + virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, + GLenum* const out_glError) override; +}; + +class TexUnpackSurface : public TexUnpackBlob +{ +public: + const RefPtr mSurf; + const bool mIsAlphaPremult; + + TexUnpackSurface(const RefPtr& surf, bool isAlphaPremult); + virtual ~TexUnpackSurface() override; + + virtual bool ValidateUnpack(WebGLContext* webgl, const char* funcName, bool isFunc3D, + const webgl::PackingInfo& pi) override + { + return true; + } + + virtual void TexOrSubImage(bool isSubImage, bool needsRespec, const char* funcName, + WebGLTexture* tex, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, + GLenum* const out_glError) override; + +protected: + static bool ConvertSurface(WebGLContext* webgl, const webgl::DriverUnpackInfo* dui, + gfx::DataSourceSurface* surf, bool isSurfAlphaPremult, + UniqueBuffer* const out_convertedBuffer, + uint8_t* const out_convertedAlignment, + bool* const out_outOfMemory); + static bool UploadDataSurface(bool isSubImage, WebGLContext* webgl, + TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, + GLsizei height, gfx::DataSourceSurface* surf, + bool isSurfAlphaPremult, GLenum* const out_glError); +}; + +} // namespace webgl +} // namespace mozilla + +#endif // TEX_UNPACK_BLOB_H_ diff --git a/dom/canvas/WebGL1Context.cpp b/dom/canvas/WebGL1Context.cpp index 276a2f3fbf..cb4caa7f91 100644 --- a/dom/canvas/WebGL1Context.cpp +++ b/dom/canvas/WebGL1Context.cpp @@ -27,9 +27,9 @@ WebGL1Context::~WebGL1Context() } UniquePtr -WebGL1Context::CreateFormatUsage() const +WebGL1Context::CreateFormatUsage(gl::GLContext* gl) const { - return webgl::FormatUsageAuthority::CreateForWebGL1(); + return webgl::FormatUsageAuthority::CreateForWebGL1(gl); } JSObject* diff --git a/dom/canvas/WebGL1Context.h b/dom/canvas/WebGL1Context.h index 2d2698dd70..eb6e0784c9 100644 --- a/dom/canvas/WebGL1Context.h +++ b/dom/canvas/WebGL1Context.h @@ -18,7 +18,9 @@ public: private: WebGL1Context(); - virtual UniquePtr CreateFormatUsage() const override; + + virtual UniquePtr + CreateFormatUsage(gl::GLContext* gl) const override; public: virtual ~WebGL1Context(); diff --git a/dom/canvas/WebGL2Context.cpp b/dom/canvas/WebGL2Context.cpp index 346d7a096e..7a695c0c8c 100644 --- a/dom/canvas/WebGL2Context.cpp +++ b/dom/canvas/WebGL2Context.cpp @@ -29,9 +29,9 @@ WebGL2Context::~WebGL2Context() } UniquePtr -WebGL2Context::CreateFormatUsage() const +WebGL2Context::CreateFormatUsage(gl::GLContext* gl) const { - return webgl::FormatUsageAuthority::CreateForWebGL2(); + return webgl::FormatUsageAuthority::CreateForWebGL2(gl); } /*static*/ bool @@ -55,20 +55,6 @@ WebGL2Context::WrapObject(JSContext* cx, JS::Handle givenProto) //////////////////////////////////////////////////////////////////////////////// // WebGL 2 initialisation -// These WebGL 1 extensions are natively supported by WebGL 2. -static const WebGLExtensionID kNativelySupportedExtensions[] = { - WebGLExtensionID::ANGLE_instanced_arrays, - WebGLExtensionID::EXT_blend_minmax, - WebGLExtensionID::EXT_sRGB, - WebGLExtensionID::OES_element_index_uint, - WebGLExtensionID::OES_standard_derivatives, - WebGLExtensionID::OES_texture_float_linear, - WebGLExtensionID::OES_texture_half_float_linear, - WebGLExtensionID::OES_vertex_array_object, - WebGLExtensionID::WEBGL_depth_texture, - WebGLExtensionID::WEBGL_draw_buffers -}; - static const gl::GLFeature kRequiredFeatures[] = { gl::GLFeature::blend_minmax, gl::GLFeature::clear_buffers, @@ -151,35 +137,25 @@ WebGLContext::InitWebGL2() return false; } - // ok WebGL 2 is compatible, we can enable natively supported extensions. - for (size_t i = 0; i < ArrayLength(kNativelySupportedExtensions); i++) { - EnableExtension(kNativelySupportedExtensions[i]); - - MOZ_ASSERT(IsExtensionEnabled(kNativelySupportedExtensions[i])); - } - // we initialise WebGL 2 related stuff. gl->GetUIntegerv(LOCAL_GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &mGLMaxTransformFeedbackSeparateAttribs); gl->GetUIntegerv(LOCAL_GL_MAX_UNIFORM_BUFFER_BINDINGS, &mGLMaxUniformBufferBindings); - if (MinCapabilityMode()) { - mGLMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE; - mGLMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS; - } else { - gl->fGetIntegerv(LOCAL_GL_MAX_3D_TEXTURE_SIZE, - (GLint*) &mGLMax3DTextureSize); - gl->fGetIntegerv(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, - (GLint*) &mGLMaxArrayTextureLayers); - } - mBoundTransformFeedbackBuffers.SetLength(mGLMaxTransformFeedbackSeparateAttribs); mBoundUniformBuffers.SetLength(mGLMaxUniformBufferBindings); mDefaultTransformFeedback = new WebGLTransformFeedback(this, 0); mBoundTransformFeedback = mDefaultTransformFeedback; + if (!gl->IsGLES()) { + // Desktop OpenGL requires the following to be enabled in order to + // support sRGB operations on framebuffers. + gl->MakeCurrent(); + gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT); + } + return true; } diff --git a/dom/canvas/WebGL2Context.h b/dom/canvas/WebGL2Context.h index f3d11cac14..d424a765f6 100644 --- a/dom/canvas/WebGL2Context.h +++ b/dom/canvas/WebGL2Context.h @@ -8,12 +8,6 @@ #include "WebGLContext.h" -/* - * Minimum value constants define in 6.2 State Tables of OpenGL ES - 3.0.4 - */ -#define MINVALUE_GL_MAX_3D_TEXTURE_SIZE 256 -#define MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS 256 - namespace mozilla { class ErrorResult; @@ -96,37 +90,47 @@ public: // ------------------------------------------------------------------------- // Texture objects - WebGL2ContextTextures.cpp - void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); - void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, - GLsizei depth); - void TexImage3D(GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLenum format, GLenum type, - const dom::Nullable& pixels, - ErrorResult& rv); - void TexSubImage3D(GLenum target, GLint level, - GLint xoffset, GLint yoffset, GLint zoffset, - GLsizei width, GLsizei height, GLsizei depth, - GLenum format, GLenum type, const dom::Nullable& pixels, - ErrorResult& rv); - void TexSubImage3D(GLenum target, GLint level, - GLint xoffset, GLint yoffset, GLint zoffset, - GLenum format, GLenum type, dom::ImageData* data, - ErrorResult& rv); - template - void TexSubImage3D(GLenum target, GLint level, - GLint xoffset, GLint yoffset, GLint zoffset, - GLenum format, GLenum type, ElementType& elt, ErrorResult& rv) - {} + void TexStorage2D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, + GLsizei height); + void TexStorage3D(GLenum target, GLsizei levels, GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei depth); + void TexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, + GLsizei height, GLsizei depth, GLint border, GLenum unpackFormat, + GLenum unpackType, + const dom::Nullable& pixels); + void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, + GLenum unpackFormat, GLenum unpackType, + const dom::Nullable& pixels, + ErrorResult& out_rv); + void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLenum unpackFormat, GLenum unpackType, + dom::ImageData* data, ErrorResult& out_rv); +protected: + void TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLenum unpackFormat, GLenum unpackType, + dom::Element* elem, ErrorResult* const out_rv); +public: + template + inline void + TexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, + GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_rv) + { + TexSubImage3D(target, level, xOffset, yOffset, zOffset, unpackFormat, unpackType, + &elem, &out_rv); + } - void CopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - GLint x, GLint y, GLsizei width, GLsizei height); - void CompressedTexImage3D(GLenum target, GLint level, GLenum internalformat, + void CopyTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLint x, GLint y, GLsizei width, + GLsizei height); + void CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& data); - void CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - GLsizei width, GLsizei height, GLsizei depth, - GLenum format, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& data); + GLint border, + const dom::ArrayBufferViewOrSharedArrayBufferView& data); + void CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, GLenum sizedUnpackFormat, + const dom::ArrayBufferViewOrSharedArrayBufferView& data); // ------------------------------------------------------------------------- @@ -383,7 +387,8 @@ public: private: WebGL2Context(); - virtual UniquePtr CreateFormatUsage() const override; + virtual UniquePtr + CreateFormatUsage(gl::GLContext* gl) const override; virtual bool IsTexParamValid(GLenum pname) const override; diff --git a/dom/canvas/WebGL2ContextFramebuffers.cpp b/dom/canvas/WebGL2ContextFramebuffers.cpp index abf5c3eb2d..22eefac2c1 100644 --- a/dom/canvas/WebGL2ContextFramebuffers.cpp +++ b/dom/canvas/WebGL2ContextFramebuffers.cpp @@ -13,107 +13,12 @@ namespace mozilla { -using gl::GLContext; -using gl::GLFormats; -using webgl::EffectiveFormat; -using webgl::FormatInfo; -using webgl::ComponentType; - -// Returns one of FLOAT, INT, UNSIGNED_INT. -// Fixed-points (normalized ints) are considered FLOAT. -static GLenum -ValueTypeForFormat(GLenum internalFormat) -{ - switch (internalFormat) { - // Fixed-point - case LOCAL_GL_R8: - case LOCAL_GL_RG8: - case LOCAL_GL_RGB565: - case LOCAL_GL_RGB8: - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGB5_A1: - case LOCAL_GL_RGBA8: - case LOCAL_GL_RGB10_A2: - case LOCAL_GL_ALPHA8: - case LOCAL_GL_LUMINANCE8: - case LOCAL_GL_LUMINANCE8_ALPHA8: - case LOCAL_GL_SRGB8: - case LOCAL_GL_SRGB8_ALPHA8: - case LOCAL_GL_R8_SNORM: - case LOCAL_GL_RG8_SNORM: - case LOCAL_GL_RGB8_SNORM: - case LOCAL_GL_RGBA8_SNORM: - - // Floating-point - case LOCAL_GL_R16F: - case LOCAL_GL_RG16F: - case LOCAL_GL_RGB16F: - case LOCAL_GL_RGBA16F: - case LOCAL_GL_ALPHA16F_EXT: - case LOCAL_GL_LUMINANCE16F_EXT: - case LOCAL_GL_LUMINANCE_ALPHA16F_EXT: - - case LOCAL_GL_R32F: - case LOCAL_GL_RG32F: - case LOCAL_GL_RGB32F: - case LOCAL_GL_RGBA32F: - case LOCAL_GL_ALPHA32F_EXT: - case LOCAL_GL_LUMINANCE32F_EXT: - case LOCAL_GL_LUMINANCE_ALPHA32F_EXT: - - case LOCAL_GL_R11F_G11F_B10F: - case LOCAL_GL_RGB9_E5: - return LOCAL_GL_FLOAT; - - // Int - case LOCAL_GL_R8I: - case LOCAL_GL_RG8I: - case LOCAL_GL_RGB8I: - case LOCAL_GL_RGBA8I: - - case LOCAL_GL_R16I: - case LOCAL_GL_RG16I: - case LOCAL_GL_RGB16I: - case LOCAL_GL_RGBA16I: - - case LOCAL_GL_R32I: - case LOCAL_GL_RG32I: - case LOCAL_GL_RGB32I: - case LOCAL_GL_RGBA32I: - return LOCAL_GL_INT; - - // Unsigned int - case LOCAL_GL_R8UI: - case LOCAL_GL_RG8UI: - case LOCAL_GL_RGB8UI: - case LOCAL_GL_RGBA8UI: - - case LOCAL_GL_R16UI: - case LOCAL_GL_RG16UI: - case LOCAL_GL_RGB16UI: - case LOCAL_GL_RGBA16UI: - - case LOCAL_GL_R32UI: - case LOCAL_GL_RG32UI: - case LOCAL_GL_RGB32UI: - case LOCAL_GL_RGBA32UI: - - case LOCAL_GL_RGB10_A2UI: - return LOCAL_GL_UNSIGNED_INT; - - default: - MOZ_CRASH("Bad `internalFormat`."); - } -} - -// ------------------------------------------------------------------------- -// Framebuffer objects - static bool GetFBInfoForBlit(const WebGLFramebuffer* fb, WebGLContext* webgl, const char* const fbInfo, GLsizei* const out_samples, - GLenum* const out_colorFormat, GLenum* const out_depthFormat, - GLenum* const out_stencilFormat) + const webgl::FormatInfo** const out_colorFormat, + const webgl::FormatInfo** const out_depthFormat, + const webgl::FormatInfo** const out_stencilFormat) { auto status = fb->PrecheckFramebufferStatus(); if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { @@ -123,36 +28,58 @@ GetFBInfoForBlit(const WebGLFramebuffer* fb, WebGLContext* webgl, } *out_samples = 1; // TODO + *out_colorFormat = nullptr; + *out_depthFormat = nullptr; + *out_stencilFormat = nullptr; if (fb->ColorAttachment(0).IsDefined()) { - const auto& attachement = fb->ColorAttachment(0); - *out_colorFormat = attachement.EffectiveInternalFormat().get(); - } else { - *out_colorFormat = 0; + const auto& attachment = fb->ColorAttachment(0); + *out_colorFormat = attachment.Format()->format; } if (fb->DepthStencilAttachment().IsDefined()) { - const auto& attachement = fb->DepthStencilAttachment(); - *out_depthFormat = attachement.EffectiveInternalFormat().get(); + const auto& attachment = fb->DepthStencilAttachment(); + *out_depthFormat = attachment.Format()->format; *out_stencilFormat = *out_depthFormat; } else { if (fb->DepthAttachment().IsDefined()) { - const auto& attachement = fb->DepthAttachment(); - *out_depthFormat = attachement.EffectiveInternalFormat().get(); - } else { - *out_depthFormat = 0; + const auto& attachment = fb->DepthAttachment(); + *out_depthFormat = attachment.Format()->format; } if (fb->StencilAttachment().IsDefined()) { - const auto& attachement = fb->StencilAttachment(); - *out_stencilFormat = attachement.EffectiveInternalFormat().get(); - } else { - *out_stencilFormat = 0; + const auto& attachment = fb->StencilAttachment(); + *out_stencilFormat = attachment.Format()->format; } } return true; } +static void +GetBackbufferFormats(const WebGLContextOptions& options, + const webgl::FormatInfo** const out_color, + const webgl::FormatInfo** const out_depth, + const webgl::FormatInfo** const out_stencil) +{ + const auto effFormat = options.alpha ? webgl::EffectiveFormat::RGBA8 + : webgl::EffectiveFormat::RGB8; + *out_color = webgl::GetFormat(effFormat); + + *out_depth = nullptr; + *out_stencil = nullptr; + if (options.depth && options.stencil) { + *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH24_STENCIL8); + *out_stencil = *out_depth; + } else { + if (options.depth) { + *out_depth = webgl::GetFormat(webgl::EffectiveFormat::DEPTH_COMPONENT16); + } + if (options.stencil) { + *out_stencil = webgl::GetFormat(webgl::EffectiveFormat::STENCIL_INDEX8); + } + } +} + void WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, @@ -196,9 +123,9 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY } GLsizei srcSamples; - GLenum srcColorFormat = 0; - GLenum srcDepthFormat = 0; - GLenum srcStencilFormat = 0; + const webgl::FormatInfo* srcColorFormat = nullptr; + const webgl::FormatInfo* srcDepthFormat = nullptr; + const webgl::FormatInfo* srcStencilFormat = nullptr; if (mBoundReadFramebuffer) { if (!GetFBInfoForBlit(mBoundReadFramebuffer, this, "READ_FRAMEBUFFER", @@ -210,26 +137,14 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY } else { srcSamples = 1; // Always 1. - // TODO: Don't hardcode these. - srcColorFormat = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8; - - if (mOptions.depth && mOptions.stencil) { - srcDepthFormat = LOCAL_GL_DEPTH24_STENCIL8; - srcStencilFormat = srcDepthFormat; - } else { - if (mOptions.depth) { - srcDepthFormat = LOCAL_GL_DEPTH_COMPONENT16; - } - if (mOptions.stencil) { - srcStencilFormat = LOCAL_GL_STENCIL_INDEX8; - } - } + GetBackbufferFormats(mOptions, &srcColorFormat, &srcDepthFormat, + &srcStencilFormat); } GLsizei dstSamples; - GLenum dstColorFormat = 0; - GLenum dstDepthFormat = 0; - GLenum dstStencilFormat = 0; + const webgl::FormatInfo* dstColorFormat = nullptr; + const webgl::FormatInfo* dstDepthFormat = nullptr; + const webgl::FormatInfo* dstStencilFormat = nullptr; if (mBoundDrawFramebuffer) { if (!GetFBInfoForBlit(mBoundDrawFramebuffer, this, "DRAW_FRAMEBUFFER", @@ -241,36 +156,39 @@ WebGL2Context::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY } else { dstSamples = gl->Screen()->Samples(); - // TODO: Don't hardcode these. - dstColorFormat = mOptions.alpha ? LOCAL_GL_RGBA8 : LOCAL_GL_RGB8; - - if (mOptions.depth && mOptions.stencil) { - dstDepthFormat = LOCAL_GL_DEPTH24_STENCIL8; - dstStencilFormat = dstDepthFormat; - } else { - if (mOptions.depth) { - dstDepthFormat = LOCAL_GL_DEPTH_COMPONENT16; - } - if (mOptions.stencil) { - dstStencilFormat = LOCAL_GL_STENCIL_INDEX8; - } - } + GetBackbufferFormats(mOptions, &dstColorFormat, &dstDepthFormat, + &dstStencilFormat); } - if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { - const GLenum srcColorType = srcColorFormat ? ValueTypeForFormat(srcColorFormat) - : 0; - const GLenum dstColorType = dstColorFormat ? ValueTypeForFormat(dstColorFormat) - : 0; - if (dstColorType != srcColorType) { - ErrorInvalidOperation("blitFramebuffer: Color buffer value type" + const auto fnSignlessType = [](const webgl::FormatInfo* format) + -> webgl::ComponentType + { + if (!format) + return webgl::ComponentType::None; + + switch (format->componentType) { + case webgl::ComponentType::UInt: + return webgl::ComponentType::Int; + + case webgl::ComponentType::NormUInt: + return webgl::ComponentType::NormInt; + + default: + return format->componentType; + } + }; + + const auto srcType = fnSignlessType(srcColorFormat); + const auto dstType = fnSignlessType(dstColorFormat); + + if (srcType != dstType) { + ErrorInvalidOperation("blitFramebuffer: Color buffer format component type" " mismatch."); return; } - const bool srcIsInt = srcColorType == LOCAL_GL_INT || - srcColorType == LOCAL_GL_UNSIGNED_INT; + const bool srcIsInt = (srcType == webgl::ComponentType::Int); if (srcIsInt && filter != LOCAL_GL_NEAREST) { ErrorInvalidOperation("blitFramebuffer: Integer read buffers can only" " be filtered with NEAREST."); @@ -378,16 +296,16 @@ WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, if (level < 0) return ErrorInvalidValue("framebufferTextureLayer: layer must be >= 0."); - switch (texture->Target()) { + switch (texture->Target().get()) { case LOCAL_GL_TEXTURE_3D: - if ((GLuint) layer >= mGLMax3DTextureSize) { + if (uint32_t(layer) >= mImplMax3DTextureSize) { return ErrorInvalidValue("framebufferTextureLayer: layer must be < " "MAX_3D_TEXTURE_SIZE"); } break; case LOCAL_GL_TEXTURE_2D_ARRAY: - if ((GLuint) layer >= mGLMaxArrayTextureLayers) { + if (uint32_t(layer) >= mImplMaxArrayTextureLayers) { return ErrorInvalidValue("framebufferTextureLayer: layer must be < " "MAX_ARRAY_TEXTURE_LAYERS"); } @@ -397,9 +315,6 @@ WebGL2Context::FramebufferTextureLayer(GLenum target, GLenum attachment, return ErrorInvalidOperation("framebufferTextureLayer: texture must be an " "existing 3D texture, or a 2D texture array."); } - } else { - return ErrorInvalidOperation("framebufferTextureLayer: texture must be an " - "existing 3D texture, or a 2D texture array."); } WebGLFramebuffer* fb; @@ -430,96 +345,10 @@ WebGL2Context::GetFramebufferAttachmentParameter(JSContext* cx, GLenum target, GLenum attachment, GLenum pname, - ErrorResult& rv) + ErrorResult& out_error) { - if (IsContextLost()) - return JS::NullValue(); - - // OpenGL ES 3.0.4 (August 27, 2014) 6.1. QUERYING GL STATE 240 - // "getFramebufferAttachmentParamter returns information about attachments of a bound - // framebuffer object. target must be DRAW_FRAMEBUFFER, READ_FRAMEBUFFER, or - // FRAMEBUFFER." - - if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter")) - return JS::NullValue(); - - // FRAMEBUFFER is equivalent to DRAW_FRAMEBUFFER. - if (target == LOCAL_GL_FRAMEBUFFER) - target = LOCAL_GL_DRAW_FRAMEBUFFER; - - WebGLFramebuffer* boundFB = nullptr; - switch (target) { - case LOCAL_GL_DRAW_FRAMEBUFFER: boundFB = mBoundDrawFramebuffer; break; - case LOCAL_GL_READ_FRAMEBUFFER: boundFB = mBoundReadFramebuffer; break; - } - - if (boundFB) { - return boundFB->GetAttachmentParameter(cx, attachment, pname, rv); - } - - // Handle default FB - const gl::GLFormats& formats = gl->GetGLFormats(); - GLenum internalFormat = LOCAL_GL_NONE; - - /* If the default framebuffer is bound to target, then attachment must be BACK, - identifying the color buffer; DEPTH, identifying the depth buffer; or STENCIL, - identifying the stencil buffer. */ - switch (attachment) { - case LOCAL_GL_BACK: - internalFormat = formats.color_texInternalFormat; - break; - - case LOCAL_GL_DEPTH: - internalFormat = formats.depth; - break; - - case LOCAL_GL_STENCIL: - internalFormat = formats.stencil; - break; - - default: - ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only query " - "attachment BACK, DEPTH, or STENCIL from default " - "framebuffer"); - return JS::NullValue(); - } - - const FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat); - MOZ_RELEASE_ASSERT(info); - EffectiveFormat effectiveFormat = info->effectiveFormat; - - switch (pname) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - return JS::Int32Value(webgl::GetComponentSize(effectiveFormat, pname)); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT && - pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) - { - ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying " - "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against " - "DEPTH_STENCIL_ATTACHMENT is an error."); - return JS::NullValue(); - } - - return JS::Int32Value(webgl::GetComponentType(effectiveFormat)); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: - return JS::Int32Value(webgl::GetColorEncoding(effectiveFormat)); - } - - /* Any combinations of framebuffer type and pname not described above will generate an - INVALID_ENUM error. */ - ErrorInvalidEnum("getFramebufferAttachmentParameter: Invalid combination of "); - return JS::NullValue(); + return WebGLContext::GetFramebufferAttachmentParameter(cx, target, attachment, pname, + out_error); } // Map attachments intended for the default buffer, to attachments for a non- @@ -557,12 +386,14 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, const dom::Sequence& attachments, ErrorResult& rv) { + const char funcName[] = "invalidateSubFramebuffer"; + if (IsContextLost()) return; MakeContextCurrent(); - if (!ValidateFramebufferTarget(target, "framebufferRenderbuffer")) + if (!ValidateFramebufferTarget(target, funcName)) return; const WebGLFramebuffer* fb; @@ -583,9 +414,10 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, MOZ_CRASH("Bad target."); } + const bool badColorAttachmentIsInvalidOp = true; for (size_t i = 0; i < attachments.Length(); i++) { - if (!ValidateFramebufferAttachment(fb, attachments[i], - "invalidateFramebuffer")) + if (!ValidateFramebufferAttachment(fb, attachments[i], funcName, + badColorAttachmentIsInvalidOp)) { return; } @@ -594,8 +426,7 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, // InvalidateFramebuffer is a hint to the driver. Should be OK to // skip calls if not supported, for example by OSX 10.9 GL // drivers. - static bool invalidateFBSupported = gl->IsSupported(gl::GLFeature::invalidate_framebuffer); - if (!invalidateFBSupported) + if (!gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) return; if (!fb && !isDefaultFB) { @@ -605,7 +436,8 @@ WebGL2Context::InvalidateFramebuffer(GLenum target, return; } - gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements()); + gl->fInvalidateFramebuffer(target, tmpAttachments.Length(), + tmpAttachments.Elements()); } else { gl->fInvalidateFramebuffer(target, attachments.Length(), attachments.Elements()); } @@ -616,14 +448,21 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::Sequence= 0.", funcName); + return; + } + const WebGLFramebuffer* fb; bool isDefaultFB; switch (target) { @@ -642,9 +481,10 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::SequenceIsSupported(gl::GLFeature::invalidate_framebuffer); - if (!invalidateFBSupported) + if (!gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) return; if (!fb && !isDefaultFB) { @@ -664,11 +503,11 @@ WebGL2Context::InvalidateSubFramebuffer(GLenum target, const dom::SequencefInvalidateSubFramebuffer(target, tmpAttachments.Length(), tmpAttachments.Elements(), - x, y, width, height); + gl->fInvalidateSubFramebuffer(target, tmpAttachments.Length(), + tmpAttachments.Elements(), x, y, width, height); } else { - gl->fInvalidateSubFramebuffer(target, attachments.Length(), attachments.Elements(), - x, y, width, height); + gl->fInvalidateSubFramebuffer(target, attachments.Length(), + attachments.Elements(), x, y, width, height); } } @@ -679,7 +518,7 @@ WebGL2Context::ReadBuffer(GLenum mode) return; const bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 && - mode <= LastColorAttachment()); + mode <= LastColorAttachmentEnum()); if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK && !isColorAttachment) { ErrorInvalidEnum("readBuffer: `mode` must be one of NONE, BACK, or " diff --git a/dom/canvas/WebGL2ContextState.cpp b/dom/canvas/WebGL2ContextState.cpp index e41b7009e1..741e1001bc 100644 --- a/dom/canvas/WebGL2ContextState.cpp +++ b/dom/canvas/WebGL2ContextState.cpp @@ -59,8 +59,6 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) /* fall through */ /* GLint */ - case LOCAL_GL_MAX_3D_TEXTURE_SIZE: - case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS: case LOCAL_GL_MAX_COMBINED_UNIFORM_BLOCKS: case LOCAL_GL_MAX_ELEMENTS_INDICES: case LOCAL_GL_MAX_ELEMENTS_VERTICES: @@ -89,6 +87,12 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::Int32Value(val); } + case LOCAL_GL_MAX_3D_TEXTURE_SIZE: + return JS::Int32Value(mImplMax3DTextureSize); + + case LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS: + return JS::Int32Value(mImplMaxArrayTextureLayers); + case LOCAL_GL_MAX_VARYING_COMPONENTS: { // On OS X Core Profile this is buggy. The spec says that the // value is 4 * GL_MAX_VARYING_VECTORS @@ -151,9 +155,7 @@ WebGL2Context::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return WebGLObjectAsJSValue(cx, mBoundSamplers[mActiveTexture].get(), rv); case LOCAL_GL_TEXTURE_BINDING_2D_ARRAY: - // TODO: Implement gl.TEXTURE_2D_ARRAY - // return WebGLObjectAsJSValue(cx, mBound2DTextureArrays[mActiveTexture].get(), rv); - return JS::NullValue(); + return WebGLObjectAsJSValue(cx, mBound2DArrayTextures[mActiveTexture].get(), rv); case LOCAL_GL_TEXTURE_BINDING_3D: return WebGLObjectAsJSValue(cx, mBound3DTextures[mActiveTexture].get(), rv); diff --git a/dom/canvas/WebGL2ContextTextures.cpp b/dom/canvas/WebGL2ContextTextures.cpp index 3af2110252..e41a9aee43 100644 --- a/dom/canvas/WebGL2ContextTextures.cpp +++ b/dom/canvas/WebGL2ContextTextures.cpp @@ -11,15 +11,19 @@ namespace mozilla { void -WebGL2Context::TexStorage2D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) +WebGL2Context::TexStorage2D(GLenum rawTexTarget, GLsizei levels, GLenum internalFormat, + GLsizei width, GLsizei height) { const char funcName[] = "TexStorage2D"; - TexTarget texTarget; + const uint8_t funcDims = 2; + + TexTarget target; WebGLTexture* tex; - if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex)) + if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &target, &tex)) return; - tex->TexStorage2D(texTarget, levels, internalFormat, width, height); + const GLsizei depth = 1; + tex->TexStorage(funcName, target, levels, internalFormat, width, height, depth); } void @@ -27,88 +31,178 @@ WebGL2Context::TexStorage3D(GLenum rawTexTarget, GLsizei levels, GLenum internal GLsizei width, GLsizei height, GLsizei depth) { const char funcName[] = "texStorage3D"; - TexTarget texTarget; + const uint8_t funcDims = 3; + + TexTarget target; WebGLTexture* tex; - if (!ValidateTexTarget(this, rawTexTarget, funcName, &texTarget, &tex)) + if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &target, &tex)) return; - tex->TexStorage3D(texTarget, levels, internalFormat, width, height, depth); + tex->TexStorage(funcName, target, levels, internalFormat, width, height, depth); } void WebGL2Context::TexImage3D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLenum unpackFormat, GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult& out_rv) + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum unpackFormat, GLenum unpackType, + const dom::Nullable& maybeView) { const char funcName[] = "texImage3D"; - TexImageTarget texImageTarget; + const uint8_t funcDims = 3; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex)) + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) { return; } - tex->TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border, - unpackFormat, unpackType, maybeView, &out_rv); + const bool isSubImage = false; + const GLint xOffset = 0; + const GLint yOffset = 0; + const GLint zOffset = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, width, height, depth, border, unpackFormat, + unpackType, maybeView); } void -WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, - GLint xOffset, GLint yOffset, GLint zOffset, - GLsizei width, GLsizei height, GLsizei depth, - GLenum unpackFormat, GLenum unpackType, +WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, GLenum unpackFormat, GLenum unpackType, const dom::Nullable& maybeView, - ErrorResult& out_rv) + ErrorResult& /*out_rv*/) { const char funcName[] = "texSubImage3D"; - TexImageTarget texImageTarget; + const uint8_t funcDims = 3; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, funcName, &texImageTarget, &tex)) + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) { return; } - tex->TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height, - depth, unpackFormat, unpackType, maybeView, &out_rv); - + const bool isSubImage = true; + const GLenum internalFormat = 0; + const GLint border = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, width, height, depth, border, unpackFormat, + unpackType, maybeView); } void -WebGL2Context::TexSubImage3D(GLenum target, GLint level, - GLint xOffset, GLint yOffset, GLint zOffset, - GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, - ErrorResult& out_rv) +WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum unpackFormat, + GLenum unpackType, dom::ImageData* imageData, + ErrorResult& /*out_rv*/) { - GenerateWarning("texSubImage3D: Not implemented."); + const char funcName[] = "texSubImage3D"; + const uint8_t funcDims = 3; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + const bool isSubImage = true; + const GLenum internalFormat = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, unpackFormat, unpackType, imageData); } void -WebGL2Context::CopyTexSubImage3D(GLenum target, GLint level, - GLint xOffset, GLint yOffset, GLint zOffset, - GLint x, GLint y, GLsizei width, GLsizei height) +WebGL2Context::TexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum unpackFormat, + GLenum unpackType, dom::Element* elem, + ErrorResult* const out_rv) { - GenerateWarning("copyTexSubImage3D: Not implemented."); + const char funcName[] = "texSubImage3D"; + const uint8_t funcDims = 3; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + const bool isSubImage = true; + const GLenum internalFormat = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, unpackFormat, unpackType, elem, out_rv); } void -WebGL2Context::CompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& view) +WebGL2Context::CompressedTexImage3D(GLenum rawTexImageTarget, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, + const dom::ArrayBufferViewOrSharedArrayBufferView& view) { - GenerateWarning("compressedTexImage3D: Not implemented."); + const char funcName[] = "compressedTexImage3D"; + const uint8_t funcDims = 3; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + tex->CompressedTexImage(funcName, target, level, internalFormat, width, height, depth, + border, view); } void -WebGL2Context::CompressedTexSubImage3D(GLenum target, GLint level, GLint xOffset, GLint yOffset, GLint zOffset, +WebGL2Context::CompressedTexSubImage3D(GLenum rawTexImageTarget, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, - GLenum unpackFormat, GLsizei imageSize, const dom::ArrayBufferViewOrSharedArrayBufferView& view) + GLenum sizedUnpackFormat, + const dom::ArrayBufferViewOrSharedArrayBufferView& view) { - GenerateWarning("compressedTexSubImage3D: Not implemented."); + const char funcName[] = "compressedTexSubImage3D"; + const uint8_t funcDims = 3; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + tex->CompressedTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, width, + height, depth, sizedUnpackFormat, view); } -bool +void +WebGL2Context::CopyTexSubImage3D(GLenum rawTexImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLint x, GLint y, + GLsizei width, GLsizei height) +{ + const char funcName[] = "copyTexSubImage3D"; + const uint8_t funcDims = 3; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width, + height); +} + +/*virtual*/ bool WebGL2Context::IsTexParamValid(GLenum pname) const { switch (pname) { diff --git a/dom/canvas/WebGLBuffer.h b/dom/canvas/WebGLBuffer.h index 87d8f98324..39bb7e5b41 100644 --- a/dom/canvas/WebGLBuffer.h +++ b/dom/canvas/WebGLBuffer.h @@ -55,7 +55,7 @@ public: bool IsElementArrayUsedWithMultipleTypes() const; WebGLContext* GetParentObject() const { - return Context(); + return mContext; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle givenProto) override; diff --git a/dom/canvas/WebGLContext.cpp b/dom/canvas/WebGLContext.cpp index 11b2434a16..e3a3ce2884 100644 --- a/dom/canvas/WebGLContext.cpp +++ b/dom/canvas/WebGLContext.cpp @@ -95,6 +95,10 @@ WebGLContextOptions::WebGLContextOptions() alpha = false; } + +/*static*/ const uint32_t WebGLContext::kMinMaxColorAttachments = 4; +/*static*/ const uint32_t WebGLContext::kMinMaxDrawBuffers = 4; + WebGLContext::WebGLContext() : WebGLContextUnchecked(nullptr) , mBypassShaderValidation(false) @@ -110,11 +114,6 @@ WebGLContext::WebGLContext() mOptionsFrozen = false; mActiveTexture = 0; - mPixelStoreFlipY = false; - mPixelStorePremultiplyAlpha = false; - mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL; - - mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded; mVertexAttrib0Vector[0] = 0; mVertexAttrib0Vector[1] = 0; @@ -138,31 +137,6 @@ WebGLContext::WebGLContext() mScissorTestEnabled = 0; mStencilTestEnabled = 0; - // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays, - // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes. - mGLMaxVertexAttribs = 0; - mGLMaxTextureUnits = 0; - mGLMaxTextureSize = 0; - mGLMaxTextureSizeLog2 = 0; - mGLMaxCubeMapTextureSize = 0; - mGLMaxCubeMapTextureSizeLog2 = 0; - mGLMaxRenderbufferSize = 0; - mGLMaxTextureImageUnits = 0; - mGLMaxVertexTextureImageUnits = 0; - mGLMaxVaryingVectors = 0; - mGLMaxFragmentUniformVectors = 0; - mGLMaxVertexUniformVectors = 0; - mGLMaxColorAttachments = 1; - mGLMaxDrawBuffers = 1; - mGLMaxTransformFeedbackSeparateAttribs = 0; - mGLMaxUniformBufferBindings = 0; - mGLMax3DTextureSize = 0; - mGLMaxArrayTextureLayers = 0; - - // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13 - mPixelStorePackAlignment = 4; - mPixelStoreUnpackAlignment = 4; - if (NS_IsMainThread()) { // XXX mtseng: bug 709490, not thread safe WebGLMemoryTracker::AddWebGLContext(this); @@ -222,6 +196,7 @@ WebGLContext::DestroyResourcesAndContext() mBound2DTextures.Clear(); mBoundCubeMapTextures.Clear(); mBound3DTextures.Clear(); + mBound2DArrayTextures.Clear(); mBoundSamplers.Clear(); mBoundArrayBuffer = nullptr; mBoundCopyReadBuffer = nullptr; @@ -265,10 +240,14 @@ WebGLContext::DestroyResourcesAndContext() while (!mTransformFeedbacks.isEmpty()) mTransformFeedbacks.getLast()->DeleteOnce(); - mBlackOpaqueTexture2D = nullptr; - mBlackOpaqueTextureCubeMap = nullptr; - mBlackTransparentTexture2D = nullptr; - mBlackTransparentTextureCubeMap = nullptr; + mFakeBlack_2D_0000 = nullptr; + mFakeBlack_2D_0001 = nullptr; + mFakeBlack_CubeMap_0000 = nullptr; + mFakeBlack_CubeMap_0001 = nullptr; + mFakeBlack_3D_0000 = nullptr; + mFakeBlack_3D_0001 = nullptr; + mFakeBlack_2D_Array_0000 = nullptr; + mFakeBlack_2D_Array_0001 = nullptr; if (mFakeVertexAttrib0BufferObject) gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject); @@ -1279,37 +1258,37 @@ WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval) void WebGLContext::ClearScreen() { - bool colorAttachmentsMask[WebGLContext::kMaxColorAttachments] = {false}; - MakeContextCurrent(); ScopedBindFramebuffer autoFB(gl, 0); - GLbitfield clearMask = LOCAL_GL_COLOR_BUFFER_BIT; + const bool changeDrawBuffers = (mDefaultFB_DrawBuffer0 != LOCAL_GL_BACK); + if (changeDrawBuffers) { + const GLenum back = LOCAL_GL_BACK; + gl->fDrawBuffers(1, &back); + } + + GLbitfield bufferBits = LOCAL_GL_COLOR_BUFFER_BIT; if (mOptions.depth) - clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT; + bufferBits |= LOCAL_GL_DEPTH_BUFFER_BIT; if (mOptions.stencil) - clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT; + bufferBits |= LOCAL_GL_STENCIL_BUFFER_BIT; - colorAttachmentsMask[0] = true; + ForceClearFramebufferWithDefaultValues(bufferBits, mNeedsFakeNoAlpha); - ForceClearFramebufferWithDefaultValues(mNeedsFakeNoAlpha, clearMask, - colorAttachmentsMask); + if (changeDrawBuffers) { + gl->fDrawBuffers(1, &mDefaultFB_DrawBuffer0); + } } void -WebGLContext::ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfield mask, - const bool colorAttachmentsMask[kMaxColorAttachments]) +WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield clearBits, + bool fakeNoAlpha) { MakeContextCurrent(); - bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT); - bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT); - bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT); - bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers); - bool shouldOverrideDrawBuffers = false; - bool usingDefaultFrameBuffer = !mBoundDrawFramebuffer; - - GLenum currentDrawBuffers[WebGLContext::kMaxColorAttachments]; + const bool initializeColorBuffer = bool(clearBits & LOCAL_GL_COLOR_BUFFER_BIT); + const bool initializeDepthBuffer = bool(clearBits & LOCAL_GL_DEPTH_BUFFER_BIT); + const bool initializeStencilBuffer = bool(clearBits & LOCAL_GL_STENCIL_BUFFER_BIT); // Fun GL fact: No need to worry about the viewport here, glViewport is just // setting up a coordinates transformation, it doesn't affect glClear at all. @@ -1320,38 +1299,6 @@ WebGLContext::ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfiel gl->fDisable(LOCAL_GL_SCISSOR_TEST); if (initializeColorBuffer) { - - if (drawBuffersIsEnabled) { - - GLenum drawBuffersCommand[WebGLContext::kMaxColorAttachments] = { LOCAL_GL_NONE }; - - for (int32_t i = 0; i < mGLMaxDrawBuffers; i++) { - GLint temp; - gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp); - currentDrawBuffers[i] = temp; - - if (colorAttachmentsMask[i]) { - drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i; - } - if (currentDrawBuffers[i] != drawBuffersCommand[i]) - shouldOverrideDrawBuffers = true; - } - - // When clearing the default framebuffer, we must be clearing only - // GL_BACK, and nothing else, or else gl may return an error. We will - // only use the first element of currentDrawBuffers in this case. - if (usingDefaultFrameBuffer) { - gl->Screen()->SetDrawBuffer(LOCAL_GL_BACK); - if (currentDrawBuffers[0] == LOCAL_GL_COLOR_ATTACHMENT0) - currentDrawBuffers[0] = LOCAL_GL_BACK; - shouldOverrideDrawBuffers = false; - } - // calling draw buffers can cause resolves on adreno drivers so - // we try to avoid calling it - if (shouldOverrideDrawBuffers) - gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand); - } - gl->fColorMask(1, 1, 1, 1); if (fakeNoAlpha) { @@ -1379,7 +1326,7 @@ WebGLContext::ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfiel } // Do the clear! - gl->fClear(mask); + gl->fClear(clearBits); // And reset! if (mScissorTestEnabled) @@ -1391,15 +1338,6 @@ WebGLContext::ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfiel // Restore GL state after clearing. if (initializeColorBuffer) { - - if (drawBuffersIsEnabled) { - if (usingDefaultFrameBuffer) { - gl->Screen()->SetDrawBuffer(currentDrawBuffers[0]); - } else if (shouldOverrideDrawBuffers) { - gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers); - } - } - gl->fColorMask(mColorWriteMask[0], mColorWriteMask[1], mColorWriteMask[2], @@ -1799,17 +1737,30 @@ WebGLContext::DidRefresh() } } -size_t -RoundUpToMultipleOf(size_t value, size_t multiple) +bool +WebGLContext::ValidateCurFBForRead(const char* funcName, + const webgl::FormatUsageInfo** const out_format, + uint32_t* const out_width, uint32_t* const out_height) { - size_t overshoot = value + multiple - 1; - return overshoot - (overshoot % multiple); -} + if (!mBoundReadFramebuffer) { + ClearBackbufferIfNeeded(); -CheckedUint32 -RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y) -{ - return ((x + y - 1) / y) * y; + // FIXME - here we're assuming that the default framebuffer is backed by + // UNSIGNED_BYTE that might not always be true, say if we had a 16bpp default + // framebuffer. + auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8 + : webgl::EffectiveFormat::RGB8; + + *out_format = mFormatUsage->GetUsage(effFormat); + MOZ_ASSERT(*out_format); + + *out_width = mWidth; + *out_height = mHeight; + return true; + } + + return mBoundReadFramebuffer->ValidateForRead(funcName, out_format, out_width, + out_height); } //////////////////////////////////////////////////////////////////////////////// @@ -1843,6 +1794,215 @@ WebGLContext::ScopedMaskWorkaround::~ScopedMaskWorkaround() } } +//////////////////////////////////////// + +ScopedUnpackReset::ScopedUnpackReset(WebGLContext* webgl) + : ScopedGLWrapper(webgl->gl) + , mWebGL(webgl) +{ + if (mWebGL->mPixelStore_UnpackAlignment != 4) mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4); + + if (mWebGL->IsWebGL2()) { + if (mWebGL->mPixelStore_UnpackRowLength != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH , 0); + if (mWebGL->mPixelStore_UnpackImageHeight != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0); + if (mWebGL->mPixelStore_UnpackSkipPixels != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , 0); + if (mWebGL->mPixelStore_UnpackSkipRows != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS , 0); + if (mWebGL->mPixelStore_UnpackSkipImages != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , 0); + + if (mWebGL->mBoundPixelUnpackBuffer) mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0); + } +} + +void +ScopedUnpackReset::UnwrapImpl() +{ + mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mWebGL->mPixelStore_UnpackAlignment); + + if (mWebGL->IsWebGL2()) { + mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH , mWebGL->mPixelStore_UnpackRowLength ); + mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mWebGL->mPixelStore_UnpackImageHeight); + mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , mWebGL->mPixelStore_UnpackSkipPixels ); + mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS , mWebGL->mPixelStore_UnpackSkipRows ); + mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , mWebGL->mPixelStore_UnpackSkipImages ); + + GLuint pbo = 0; + if (mWebGL->mBoundPixelUnpackBuffer) { + pbo = mWebGL->mBoundPixelUnpackBuffer->mGLName; + } + + mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, pbo); + } +} + +//////////////////////////////////////// + +void +Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize, + uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst, + uint32_t* const out_intSize) +{ + // Only >0 if dstStartInSrc is >0: + // 0 3 // src coords + // | [========] // dst box + // ^--^ + *out_intStartInSrc = std::max(0, dstStartInSrc); + + // Only >0 if dstStartInSrc is <0: + //-6 0 // src coords + // [=====|==] // dst box + // ^-----^ + *out_intStartInDst = std::max(0, 0 - dstStartInSrc); + + int32_t intEndInSrc = std::min(srcSize, dstStartInSrc + dstSize); + *out_intSize = std::max(0, intEndInSrc - *out_intStartInSrc); +} + +bool +ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture, + TexImageTarget target, uint32_t level, + const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset, + uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth) +{ + // This has two usecases: + // 1. Lazy zeroing of uninitialized textures: + // a. Before draw, when FakeBlack isn't viable. (TexStorage + Draw*) + // b. Before partial upload. (TexStorage + TexSubImage) + // 2. Zero subrects from out-of-bounds blits. (CopyTex(Sub)Image) + + // We have no sympathy for any of these cases. + + // "Doctor, it hurts when I do this!" "Well don't do that!" + webgl->GenerateWarning("%s: This operation requires zeroing texture data. This is" + " slow.", + funcName); + + gl::GLContext* gl = webgl->GL(); + gl->MakeCurrent(); + + ScopedUnpackReset scopedReset(webgl); + gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); // Don't bother with striding it well. + + auto compression = usage->format->compression; + if (compression) { + MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset); + MOZ_RELEASE_ASSERT(!respecifyTexture); + + auto sizedFormat = usage->format->sizedFormat; + MOZ_RELEASE_ASSERT(sizedFormat); + + const auto fnSizeInBlocks = [](CheckedUint32 pixels, uint8_t pixelsPerBlock) { + return RoundUpToMultipleOf(pixels, pixelsPerBlock) / pixelsPerBlock; + }; + + const auto widthBlocks = fnSizeInBlocks(width, compression->blockWidth); + const auto heightBlocks = fnSizeInBlocks(height, compression->blockHeight); + + CheckedUint32 checkedByteCount = compression->bytesPerBlock; + checkedByteCount *= widthBlocks; + checkedByteCount *= heightBlocks; + checkedByteCount *= depth; + + if (!checkedByteCount.isValid()) + return false; + + const size_t byteCount = checkedByteCount.value(); + + UniqueBuffer zeros = calloc(1, byteCount); + if (!zeros) + return false; + + GLenum error = DoCompressedTexSubImage(gl, target.get(), level, xOffset, yOffset, + zOffset, width, height, depth, sizedFormat, + byteCount, zeros.get()); + if (error) + return false; + + return true; + } + + const auto driverUnpackInfo = usage->idealUnpack; + MOZ_RELEASE_ASSERT(driverUnpackInfo); + + const webgl::PackingInfo packing = driverUnpackInfo->ToPacking(); + + const auto bytesPerPixel = webgl::BytesPerPixel(packing); + + CheckedUint32 checkedByteCount = bytesPerPixel; + checkedByteCount *= width; + checkedByteCount *= height; + checkedByteCount *= depth; + + if (!checkedByteCount.isValid()) + return false; + + const size_t byteCount = checkedByteCount.value(); + + UniqueBuffer zeros = calloc(1, byteCount); + if (!zeros) + return false; + + GLenum error; + if (respecifyTexture) { + MOZ_RELEASE_ASSERT(!xOffset && !yOffset && !zOffset); + error = DoTexImage(gl, target, level, driverUnpackInfo, width, height, depth, + zeros.get()); + } else { + error = DoTexSubImage(gl, target, level, xOffset, yOffset, zOffset, width, height, + depth, packing, zeros.get()); + } + if (error) + return false; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + +CheckedUint32 +WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height, + uint32_t depth, uint8_t bytesPerPixel) +{ + if (!width || !height || !depth) + return 0; + + //////////////// + + const auto& maybeRowLength = mPixelStore_UnpackRowLength; + const auto& maybeImageHeight = mPixelStore_UnpackImageHeight; + + const auto usedPixelsPerRow = CheckedUint32(mPixelStore_UnpackSkipPixels) + width; + const auto stridePixelsPerRow = (maybeRowLength ? CheckedUint32(maybeRowLength) + : usedPixelsPerRow); + + const auto usedRowsPerImage = CheckedUint32(mPixelStore_UnpackSkipRows) + height; + const auto strideRowsPerImage = (maybeImageHeight ? CheckedUint32(maybeImageHeight) + : usedRowsPerImage); + + const uint32_t skipImages = (isFunc3D ? mPixelStore_UnpackSkipImages + : 0); + const CheckedUint32 usedImages = CheckedUint32(skipImages) + depth; + + //////////////// + + CheckedUint32 strideBytesPerRow = bytesPerPixel * stridePixelsPerRow; + strideBytesPerRow = RoundUpToMultipleOf(strideBytesPerRow, + mPixelStore_UnpackAlignment); + + const CheckedUint32 strideBytesPerImage = strideBytesPerRow * strideRowsPerImage; + + //////////////// + + CheckedUint32 usedBytesPerRow = bytesPerPixel * usedPixelsPerRow; + // Don't round this to the alignment, since alignment here is really just used for + // establishing stride, particularly in WebGL 1, where you can't set ROW_LENGTH. + + CheckedUint32 totalBytes = strideBytesPerImage * (usedImages - 1); + totalBytes += strideBytesPerRow * (usedRowsPerImage - 1); + totalBytes += usedBytesPerRow; + + return totalBytes; +} + //////////////////////////////////////////////////////////////////////////////// // XPCOM goop @@ -1856,6 +2016,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext, mBound2DTextures, mBoundCubeMapTextures, mBound3DTextures, + mBound2DArrayTextures, mBoundSamplers, mBoundArrayBuffer, mBoundCopyReadBuffer, diff --git a/dom/canvas/WebGLContext.h b/dom/canvas/WebGLContext.h index 87a619cb7a..2fd743c331 100644 --- a/dom/canvas/WebGLContext.h +++ b/dom/canvas/WebGLContext.h @@ -24,6 +24,9 @@ #include "nsLayoutUtils.h" #include "nsTArray.h" #include "nsWrapperCache.h" +#include "SurfaceTypes.h" +#include "ScopedGLHelpers.h" +#include "TexUnpackBlob.h" #ifdef XP_MACOSX #include "ForceDiscreteGPUHelperCGL.h" @@ -34,6 +37,7 @@ #include "WebGLFormats.h" #include "WebGLObjectModel.h" #include "WebGLStrongTypes.h" +#include "WebGLTexture.h" // Generated #include "nsIDOMEventListener.h" @@ -65,6 +69,12 @@ class nsIDocShell; #define MINVALUE_GL_MAX_RENDERBUFFER_SIZE 1024 // Different from the spec, which sets it to 1 on page 164 #define MINVALUE_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 8 // Page 164 +/* + * Minimum value constants define in 6.2 State Tables of OpenGL ES - 3.0.4 + */ +#define MINVALUE_GL_MAX_3D_TEXTURE_SIZE 256 +#define MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS 256 + /* * WebGL-only GLenums */ @@ -76,7 +86,9 @@ class nsIDocShell; #define LOCAL_GL_UNPACK_PREMULTIPLY_ALPHA_WEBGL 0x9241 namespace mozilla { - +class ScopedCopyTexImageSource; +class ScopedResolveTexturesForDraw; +class ScopedUnpackReset; class WebGLActiveInfo; class WebGLContextLossHandler; class WebGLBuffer; @@ -109,6 +121,7 @@ class SourceSurface; namespace webgl { struct LinkedProgramInfo; class ShaderValidator; +class TexUnpackBlob; } // namespace webgl WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format); @@ -198,6 +211,9 @@ class WebGLContext UNMASKED_RENDERER_WEBGL = 0x9246 }; + static const uint32_t kMinMaxColorAttachments; + static const uint32_t kMinMaxDrawBuffers; + public: WebGLContext(); @@ -298,6 +314,8 @@ public: return mBoundCubeMapTextures[mActiveTexture]; case LOCAL_GL_TEXTURE_3D: return mBound3DTextures[mActiveTexture]; + case LOCAL_GL_TEXTURE_2D_ARRAY: + return mBound2DArrayTextures[mActiveTexture]; default: MOZ_CRASH("bad target"); } @@ -338,18 +356,11 @@ public: // all context resources to be lost. uint32_t Generation() { return mGeneration.value(); } - // Returns null if the current bound FB is not likely complete. - const WebGLRectangleObject* CurValidDrawFBRectObject() const; - const WebGLRectangleObject* CurValidReadFBRectObject() const; - - static const size_t kMaxColorAttachments = 16; - // This is similar to GLContext::ClearSafely, but tries to minimize the // amount of work it does. // It only clears the buffers we specify, and can reset its state without // first having to query anything, as WebGL knows its state at all times. - void ForceClearFramebufferWithDefaultValues(bool fakeNoAlpha, GLbitfield mask, - const bool colorAttachmentsMask[kMaxColorAttachments]); + void ForceClearFramebufferWithDefaultValues(GLbitfield bufferBits, bool fakeNoAlpha); // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'. void ClearScreen(); @@ -432,8 +443,9 @@ public: GLint level); // Framebuffer validation - bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb, - GLenum attachment, const char* funcName); + bool ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment, + const char* funcName, + bool badColorAttachmentIsInvalidOp = false); void FrontFace(GLenum mode); already_AddRefed GetActiveAttrib(WebGLProgram* prog, @@ -525,6 +537,11 @@ public: void LinkProgram(WebGLProgram* prog); void PixelStorei(GLenum pname, GLint param); void PolygonOffset(GLfloat factor, GLfloat units); +protected: + bool DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum destFormat, GLenum destType, void* destBytes, + GLenum auxReadFormat, GLenum auxReadType); +public: void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const dom::Nullable& pixels, @@ -886,46 +903,62 @@ public: GLsizei width, GLsizei height, GLint border, GLenum unpackFormat, GLenum unpackType, const dom::Nullable& maybeView, - ErrorResult& out_rv); + ErrorResult&); void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat, GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, - ErrorResult& out_rv); + ErrorResult&); void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat, GLenum unpackFormat, GLenum unpackType, dom::Element* elem, - ErrorResult* const out_rv); - + ErrorResult* const out_error); void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat, GLenum unpackType, const dom::Nullable& maybeView, - ErrorResult& out_rv); + ErrorResult&); void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset, GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, - ErrorResult& out_rv); + ErrorResult&); void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset, GLenum unpackFormat, GLenum unpackType, dom::Element* elem, - ErrorResult* const out_rv); + ErrorResult* const out_error); // Allow whatever element unpackTypes the bindings are willing to pass // us in Tex(Sub)Image2D - template - void TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat, - GLenum unpackFormat, GLenum unpackType, ElementT& elem, - ErrorResult& out_rv) + template + inline void + TexImage2D(GLenum texImageTarget, GLint level, GLenum internalFormat, + GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_error) { TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, &elem, - &out_rv); + &out_error); } - template - void TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset, - GLenum unpackFormat, GLenum unpackType, ElementT& elem, - ErrorResult& out_rv) + + template + inline void + TexSubImage2D(GLenum texImageTarget, GLint level, GLint xOffset, GLint yOffset, + GLenum unpackFormat, GLenum unpackType, T& elem, ErrorResult& out_error) { TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType, - &elem, &out_rv); + &elem, &out_error); } + // WebGLTextureUpload.cpp + bool ValidateTexImageSpecification(const char* funcName, uint8_t funcDims, + GLenum texImageTarget, GLint level, + GLsizei width, GLsizei height, GLsizei depth, + GLint border, + TexImageTarget* const out_target, + WebGLTexture** const out_texture, + WebGLTexture::ImageInfo** const out_imageInfo); + bool ValidateTexImageSelection(const char* funcName, uint8_t funcDims, + GLenum texImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, + GLsizei height, GLsizei depth, + TexImageTarget* const out_target, + WebGLTexture** const out_texture, + WebGLTexture::ImageInfo** const out_imageInfo); + // ----------------------------------------------------------------------------- // Vertices Feature (WebGLContextVertices.cpp) public: @@ -1008,7 +1041,7 @@ private: GLsizei primcount, const char* info, GLuint* out_upperBound); bool DrawInstanced_check(const char* info); - void Draw_cleanup(); + void Draw_cleanup(const char* funcName); void VertexAttrib1fv_base(GLuint index, uint32_t arrayLength, const GLfloat* ptr); @@ -1025,24 +1058,10 @@ private: // ----------------------------------------------------------------------------- // PROTECTED protected: - void SetFakeBlackStatus(WebGLContextFakeBlackStatus x) { - mFakeBlackStatus = x; - } - // Returns the current fake-black-status, except if it was Unknown, - // in which case this function resolves it first, so it never returns Unknown. - WebGLContextFakeBlackStatus ResolvedFakeBlackStatus(); - - void BindFakeBlackTextures(); - void UnbindFakeBlackTextures(); - WebGLVertexAttrib0Status WhatDoesVertexAttrib0Need(); bool DoFakeVertexAttrib0(GLuint vertexCount); void UndoFakeVertexAttrib0(); - static CheckedUint32 GetImageSize(GLsizei height, GLsizei width, - GLsizei depth, uint32_t pixelSize, - uint32_t alignment); - inline void InvalidateBufferFetching() { mBufferFetchingIsVerified = false; @@ -1073,6 +1092,7 @@ protected: void DeleteWebGLObjectsArray(nsTArray& array); GLuint mActiveTexture; + GLenum mDefaultFB_DrawBuffer0; // glGetError sources: bool mEmitContextLostErrorOnce; @@ -1087,23 +1107,36 @@ protected: // some GL constants int32_t mGLMaxVertexAttribs; int32_t mGLMaxTextureUnits; - int32_t mGLMaxTextureSize; - int32_t mGLMaxTextureSizeLog2; - int32_t mGLMaxCubeMapTextureSize; - int32_t mGLMaxCubeMapTextureSizeLog2; - int32_t mGLMaxRenderbufferSize; int32_t mGLMaxTextureImageUnits; int32_t mGLMaxVertexTextureImageUnits; int32_t mGLMaxVaryingVectors; int32_t mGLMaxFragmentUniformVectors; int32_t mGLMaxVertexUniformVectors; - int32_t mGLMaxColorAttachments; - int32_t mGLMaxDrawBuffers; uint32_t mGLMaxTransformFeedbackSeparateAttribs; GLuint mGLMaxUniformBufferBindings; GLsizei mGLMaxSamples; - GLuint mGLMax3DTextureSize; - GLuint mGLMaxArrayTextureLayers; + + // What is supported: + uint32_t mGLMaxColorAttachments; + uint32_t mGLMaxDrawBuffers; + // What we're allowing: + uint32_t mImplMaxColorAttachments; + uint32_t mImplMaxDrawBuffers; + +public: + GLenum LastColorAttachmentEnum() const { + return LOCAL_GL_COLOR_ATTACHMENT0 + mImplMaxColorAttachments - 1; + } + +protected: + + // Texture sizes are often not actually the GL values. Let's be explicit that these + // are implementation limits. + uint32_t mImplMaxTextureSize; + uint32_t mImplMaxCubeMapTextureSize; + uint32_t mImplMax3DTextureSize; + uint32_t mImplMaxArrayTextureLayers; + uint32_t mImplMaxRenderbufferSize; public: GLuint MaxVertexAttribs() const { @@ -1114,8 +1147,7 @@ public: return mGLMaxTextureUnits; } - - bool IsFormatValidForFB(GLenum sizedFormat) const; + bool IsFormatValidForFB(TexInternalFormat format) const; protected: // Represents current status of the context with respect to context loss. @@ -1152,9 +1184,11 @@ protected: WebGLExtensionBase* EnableSupportedExtension(JSContext* js, WebGLExtensionID ext); +public: // returns true if the extension has been enabled by calling getExtension. bool IsExtensionEnabled(WebGLExtensionID ext) const; +protected: // returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes) bool IsExtensionSupported(JSContext* cx, WebGLExtensionID ext) const; bool IsExtensionSupported(WebGLExtensionID ext) const; @@ -1196,8 +1230,8 @@ protected: WebGLintptr byteOffset, const char* info); bool ValidateStencilParamsForDrawCall(); - bool ValidateCopyTexImage(GLenum internalFormat, WebGLTexImageFunc func, - WebGLTexDimensions dims); + bool ValidateCopyTexImage(TexInternalFormat srcFormat, TexInternalFormat dstformat, + WebGLTexImageFunc func, WebGLTexDimensions dims); bool ValidateSamplerParameterName(GLenum pname, const char* info); bool ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat& param, const char* info); @@ -1244,6 +1278,10 @@ protected: WebGLProgram* program, const char* funcName); + bool ValidateCurFBForRead(const char* funcName, + const webgl::FormatUsageInfo** const out_format, + uint32_t* const out_width, uint32_t* const out_height); + void Invalidate(); void DestroyResourcesAndContext(); @@ -1257,31 +1295,23 @@ protected: WebGLTexelFormat dstFormat, bool dstPremultiplied, size_t dstTexelSize); - template +public: nsLayoutUtils::SurfaceFromElementResult - SurfaceFromElement(ElementType* element) { - MOZ_ASSERT(element); - + SurfaceFromElement(dom::Element* elem) + { uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE; - if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE) + + if (mPixelStore_ColorspaceConversion == LOCAL_GL_NONE) flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; - if (!mPixelStorePremultiplyAlpha) + + if (!mPixelStore_PremultiplyAlpha) flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA; - return nsLayoutUtils::SurfaceFromElement(element, flags); + RefPtr idealDrawTarget = nullptr; // Don't care for now. + return nsLayoutUtils::SurfaceFromElement(elem, flags, idealDrawTarget); } - template - nsLayoutUtils::SurfaceFromElementResult - SurfaceFromElement(ElementType& element) { - return SurfaceFromElement(&element); - } - - nsresult - SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res, - RefPtr& imageOut, - WebGLTexelFormat* format); - +protected: // Returns false if `object` is null or not valid. template bool ValidateObject(const char* info, ObjectType* object); @@ -1320,30 +1350,22 @@ private: virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0; protected: - int32_t MaxTextureSizeForTarget(TexTarget target) const { - return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize - : mGLMaxCubeMapTextureSize; - } - - int32_t - MaxTextureLevelForTexImageTarget(TexImageTarget texImageTarget) const { - const TexTarget target = TexImageTargetToTexTarget(texImageTarget); - return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSizeLog2 - : mGLMaxCubeMapTextureSizeLog2; - } - /** Like glBufferData, but if the call may change the buffer size, checks * any GL error generated by this glBufferData call and returns it. */ GLenum CheckedBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage); +public: void ForceLoseContext(bool simulateLoss = false); + +protected: void ForceRestoreContext(); nsTArray > mBound2DTextures; nsTArray > mBoundCubeMapTextures; nsTArray > mBound3DTextures; + nsTArray > mBound2DArrayTextures; nsTArray > mBoundSamplers; void ResolveTexturesForDraw() const; @@ -1351,10 +1373,6 @@ protected: WebGLRefPtr mCurrentProgram; RefPtr mActiveProgramLinkInfo; - GLenum LastColorAttachment() const { - return LOCAL_GL_COLOR_ATTACHMENT0 + mGLMaxColorAttachments - 1; - } - bool ValidateFramebufferTarget(GLenum target, const char* const info); WebGLRefPtr mBoundDrawFramebuffer; @@ -1380,34 +1398,50 @@ protected: WebGLRefPtr mDefaultVertexArray; // PixelStore parameters - uint32_t mPixelStorePackAlignment; - uint32_t mPixelStoreUnpackAlignment; - uint32_t mPixelStoreColorspaceConversion; - bool mPixelStoreFlipY; - bool mPixelStorePremultiplyAlpha; + uint32_t mPixelStore_UnpackImageHeight; + uint32_t mPixelStore_UnpackSkipImages; + uint32_t mPixelStore_UnpackRowLength; + uint32_t mPixelStore_UnpackSkipRows; + uint32_t mPixelStore_UnpackSkipPixels; + uint32_t mPixelStore_UnpackAlignment; + uint32_t mPixelStore_PackRowLength; + uint32_t mPixelStore_PackSkipRows; + uint32_t mPixelStore_PackSkipPixels; + uint32_t mPixelStore_PackAlignment; - WebGLContextFakeBlackStatus mFakeBlackStatus; + CheckedUint32 GetUnpackSize(bool isFunc3D, uint32_t width, uint32_t height, + uint32_t depth, uint8_t bytesPerPixel); + CheckedUint32 GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel, + CheckedUint32* const out_startOffset, + CheckedUint32* const out_rowStride); + + GLenum mPixelStore_ColorspaceConversion; + bool mPixelStore_FlipY; + bool mPixelStore_PremultiplyAlpha; + + //////////////////////////////////// class FakeBlackTexture { - gl::GLContext* const mGL; - GLuint mGLName; - public: - FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format); + gl::GLContext* const mGL; + const GLuint mGLName; + + FakeBlackTexture(gl::GLContext* gl, TexTarget target, FakeBlackType type); ~FakeBlackTexture(); - GLuint GLName() const { return mGLName; } }; - UniquePtr mBlackOpaqueTexture2D; - UniquePtr mBlackOpaqueTextureCubeMap; - UniquePtr mBlackTransparentTexture2D; - UniquePtr mBlackTransparentTextureCubeMap; + UniquePtr mFakeBlack_2D_0000; + UniquePtr mFakeBlack_2D_0001; + UniquePtr mFakeBlack_CubeMap_0000; + UniquePtr mFakeBlack_CubeMap_0001; + UniquePtr mFakeBlack_3D_0000; + UniquePtr mFakeBlack_3D_0001; + UniquePtr mFakeBlack_2D_Array_0000; + UniquePtr mFakeBlack_2D_Array_0001; - void - BindFakeBlackTexturesHelper(GLenum target, - const nsTArray >& boundTexturesArray, - UniquePtr& opaqueTextureScopedPtr, - UniquePtr& transparentTextureScopedPtr); + void BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack); + + //////////////////////////////////// // Generic Vertex Attributes UniquePtr mVertexAttribType; @@ -1514,10 +1548,19 @@ public: public: UniquePtr mFormatUsage; - virtual UniquePtr CreateFormatUsage() const = 0; + + virtual UniquePtr + CreateFormatUsage(gl::GLContext* gl) const = 0; // Friend list + friend class ScopedCopyTexImageSource; + friend class ScopedResolveTexturesForDraw; + friend class ScopedUnpackReset; + friend class webgl::TexUnpackBlob; + friend class webgl::TexUnpackBytes; + friend class webgl::TexUnpackSurface; friend class WebGLTexture; + friend class WebGLFBAttachPoint; friend class WebGLFramebuffer; friend class WebGLRenderbuffer; friend class WebGLProgram; @@ -1611,24 +1654,99 @@ WebGLContext::ValidateObject(const char* info, ObjectType* object) return ValidateObjectAssumeNonNull(info, object); } -size_t RoundUpToMultipleOf(size_t value, size_t multiple); +// Returns `value` rounded to the next highest multiple of `multiple`. +// AKA PadToAlignment, StrideForAlignment. +template +V +RoundUpToMultipleOf(const V& value, const M& multiple) +{ + return ((value + multiple - 1) / multiple) * multiple; +} bool -ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName, - TexTarget* const out_texTarget, WebGLTexture** const out_tex); +ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims, + GLenum rawTexTarget, TexTarget* const out_texTarget, + WebGLTexture** const out_tex); bool -ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget, - const char* funcName, TexImageTarget* const out_texImageTarget, +ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims, + GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget, WebGLTexture** const out_tex); -// Returns x rounded to the next highest multiple of y. -CheckedUint32 RoundedToNextMultipleOf(CheckedUint32 x, CheckedUint32 y); +class UniqueBuffer +{ + // Like UniquePtr<>, but for void* and malloc/calloc/free. + void* mBuffer; + +public: + UniqueBuffer() + : mBuffer(nullptr) + { } + + MOZ_IMPLICIT UniqueBuffer(void* buffer) + : mBuffer(buffer) + { } + + ~UniqueBuffer() { + free(mBuffer); + } + + UniqueBuffer(UniqueBuffer&& other) { + this->mBuffer = other.mBuffer; + other.mBuffer = nullptr; + } + + UniqueBuffer& operator =(UniqueBuffer&& other) { + free(this->mBuffer); + this->mBuffer = other.mBuffer; + other.mBuffer = nullptr; + return *this; + } + + UniqueBuffer& operator =(void* newBuffer) { + free(this->mBuffer); + this->mBuffer = newBuffer; + return *this; + } + + explicit operator bool() const { return bool(mBuffer); } + + void* get() const { return mBuffer; } + + UniqueBuffer(const UniqueBuffer& other) = delete; // construct using Move()! + void operator =(const UniqueBuffer& other) = delete; // assign using Move()! +}; + +class ScopedUnpackReset + : public gl::ScopedGLWrapper +{ + friend struct gl::ScopedGLWrapper; + +protected: + WebGLContext* const mWebGL; + +public: + explicit ScopedUnpackReset(WebGLContext* webgl); + +protected: + void UnwrapImpl(); +}; void ComputeLengthAndData(const dom::ArrayBufferViewOrSharedArrayBufferView& view, void** const out_data, size_t* const out_length, js::Scalar::Type* const out_type); +void +Intersect(uint32_t srcSize, int32_t dstStartInSrc, uint32_t dstSize, + uint32_t* const out_intStartInSrc, uint32_t* const out_intStartInDst, + uint32_t* const out_intSize); + +bool +ZeroTextureData(WebGLContext* webgl, const char* funcName, bool respecifyTexture, + TexImageTarget target, uint32_t level, + const webgl::FormatUsageInfo* usage, uint32_t xOffset, uint32_t yOffset, + uint32_t zOffset, uint32_t width, uint32_t height, uint32_t depth); + } // namespace mozilla #endif diff --git a/dom/canvas/WebGLContextDraw.cpp b/dom/canvas/WebGLContextDraw.cpp index 05d9707271..6356646bf5 100644 --- a/dom/canvas/WebGLContextDraw.cpp +++ b/dom/canvas/WebGLContextDraw.cpp @@ -23,13 +23,134 @@ namespace mozilla { // For a Tegra workaround. static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100; +//////////////////////////////////////// + +class ScopedResolveTexturesForDraw +{ + struct TexRebindRequest + { + uint32_t texUnit; + WebGLTexture* tex; + }; + + WebGLContext* const mWebGL; + std::vector mRebindRequests; + +public: + ScopedResolveTexturesForDraw(WebGLContext* webgl, const char* funcName, + bool* const out_error); + ~ScopedResolveTexturesForDraw(); +}; + +ScopedResolveTexturesForDraw::ScopedResolveTexturesForDraw(WebGLContext* webgl, + const char* funcName, + bool* const out_error) + : mWebGL(webgl) +{ + //typedef nsTArray> TexturesT; + typedef decltype(WebGLContext::mBound2DTextures) TexturesT; + + const auto fnResolveAll = [this, funcName, out_error](const TexturesT& textures) + { + const auto len = textures.Length(); + for (uint32_t texUnit = 0; texUnit < len; ++texUnit) { + WebGLTexture* tex = textures[texUnit]; + if (!tex) + continue; + + FakeBlackType fakeBlack; + *out_error |= !tex->ResolveForDraw(funcName, texUnit, &fakeBlack); + + if (fakeBlack == FakeBlackType::None) + continue; + + mWebGL->BindFakeBlack(texUnit, tex->Target(), fakeBlack); + mRebindRequests.push_back({texUnit, tex}); + } + }; + + *out_error = false; + + fnResolveAll(mWebGL->mBound2DTextures); + fnResolveAll(mWebGL->mBoundCubeMapTextures); + fnResolveAll(mWebGL->mBound3DTextures); + fnResolveAll(mWebGL->mBound2DArrayTextures); + + if (*out_error) { + mWebGL->ErrorOutOfMemory("%s: Failed to resolve textures for draw.", funcName); + } +} + +ScopedResolveTexturesForDraw::~ScopedResolveTexturesForDraw() +{ + if (!mRebindRequests.size()) + return; + + gl::GLContext* gl = mWebGL->gl; + + for (const auto& itr : mRebindRequests) { + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + itr.texUnit); + gl->fBindTexture(itr.tex->Target().get(), itr.tex->mGLName); + } + + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mWebGL->mActiveTexture); +} + +void +WebGLContext::BindFakeBlack(uint32_t texUnit, TexTarget target, FakeBlackType fakeBlack) +{ + MOZ_ASSERT(fakeBlack == FakeBlackType::RGBA0000 || + fakeBlack == FakeBlackType::RGBA0001); + + const auto fnGetSlot = [this, target, fakeBlack]() -> UniquePtr* + { + switch (fakeBlack) { + case FakeBlackType::RGBA0000: + switch (target.get()) { + case LOCAL_GL_TEXTURE_2D : return &mFakeBlack_2D_0000; + case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0000; + case LOCAL_GL_TEXTURE_3D : return &mFakeBlack_3D_0000; + case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0000; + default: return nullptr; + } + + case FakeBlackType::RGBA0001: + switch (target.get()) { + case LOCAL_GL_TEXTURE_2D : return &mFakeBlack_2D_0001; + case LOCAL_GL_TEXTURE_CUBE_MAP: return &mFakeBlack_CubeMap_0001; + case LOCAL_GL_TEXTURE_3D : return &mFakeBlack_3D_0001; + case LOCAL_GL_TEXTURE_2D_ARRAY: return &mFakeBlack_2D_Array_0001; + default: return nullptr; + } + + default: + return nullptr; + } + }; + + UniquePtr* slot = fnGetSlot(); + if (!slot) { + MOZ_CRASH("fnGetSlot failed."); + } + UniquePtr& fakeBlackTex = *slot; + + if (!fakeBlackTex) { + fakeBlackTex.reset(new FakeBlackTexture(gl, target, fakeBlack)); + } + + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit); + gl->fBindTexture(target.get(), fakeBlackTex->mGLName); + gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture); +} + +//////////////////////////////////////// + bool WebGLContext::DrawInstanced_check(const char* info) { - if ((IsWebGL2() || - IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) && - !mBufferFetchingHasPerVertex) - { + MOZ_ASSERT(IsWebGL2() || + IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)); + if (!mBufferFetchingHasPerVertex) { /* http://www.khronos.org/registry/gles/extensions/ANGLE/ANGLE_instanced_arrays.txt * If all of the enabled vertex attribute arrays that are bound to active * generic attributes in the program have a non-zero divisor, the draw @@ -111,25 +232,25 @@ WebGLContext::DrawArrays_check(GLint first, GLsizei count, GLsizei primcount, return false; } - if (!DrawInstanced_check(info)) { - return false; - } - - BindFakeBlackTextures(); - return true; } void WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count) { + const char funcName[] = "drawArrays"; if (IsContextLost()) return; - if (!ValidateDrawModeEnum(mode, "drawArrays: mode")) + if (!ValidateDrawModeEnum(mode, funcName)) return; - if (!DrawArrays_check(first, count, 1, "drawArrays")) + bool error; + ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); + if (error) + return; + + if (!DrawArrays_check(first, count, 1, funcName)) return; RunContextLossTimer(); @@ -139,19 +260,28 @@ WebGLContext::DrawArrays(GLenum mode, GLint first, GLsizei count) gl->fDrawArrays(mode, first, count); } - Draw_cleanup(); + Draw_cleanup(funcName); } void WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount) { + const char funcName[] = "drawArraysInstanced"; if (IsContextLost()) return; - if (!ValidateDrawModeEnum(mode, "drawArraysInstanced: mode")) + if (!ValidateDrawModeEnum(mode, funcName)) return; - if (!DrawArrays_check(first, count, primcount, "drawArraysInstanced")) + bool error; + ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); + if (error) + return; + + if (!DrawArrays_check(first, count, primcount, funcName)) + return; + + if (!DrawInstanced_check(funcName)) return; RunContextLossTimer(); @@ -161,7 +291,7 @@ WebGLContext::DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsiz gl->fDrawArraysInstanced(mode, first, count, primcount); } - Draw_cleanup(); + Draw_cleanup(funcName); } bool @@ -184,39 +314,40 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, } // If count is 0, there's nothing to do. - if (count == 0 || primcount == 0) { + if (count == 0 || primcount == 0) + return false; + + uint8_t bytesPerElem = 0; + switch (type) { + case LOCAL_GL_UNSIGNED_BYTE: + bytesPerElem = 1; + break; + + case LOCAL_GL_UNSIGNED_SHORT: + bytesPerElem = 2; + break; + + case LOCAL_GL_UNSIGNED_INT: + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { + bytesPerElem = 4; + } + break; + } + + if (!bytesPerElem) { + ErrorInvalidEnum("%s: Invalid `type`: 0x%04x", info, type); return false; } - CheckedUint32 checked_byteCount; - - GLsizei first = 0; - - if (type == LOCAL_GL_UNSIGNED_SHORT) { - checked_byteCount = 2 * CheckedUint32(count); - if (byteOffset % 2 != 0) { - ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_SHORT (must be a multiple of 2)", info); - return false; - } - first = byteOffset / 2; - } - else if (type == LOCAL_GL_UNSIGNED_BYTE) { - checked_byteCount = count; - first = byteOffset; - } - else if (type == LOCAL_GL_UNSIGNED_INT && IsExtensionEnabled(WebGLExtensionID::OES_element_index_uint)) { - checked_byteCount = 4 * CheckedUint32(count); - if (byteOffset % 4 != 0) { - ErrorInvalidOperation("%s: invalid byteOffset for UNSIGNED_INT (must be a multiple of 4)", info); - return false; - } - first = byteOffset / 4; - } - else { - ErrorInvalidEnum("%s: type must be UNSIGNED_SHORT or UNSIGNED_BYTE", info); + if (byteOffset % bytesPerElem != 0) { + ErrorInvalidOperation("%s: `byteOffset` must be a multiple of the size of `type`", + info); return false; } + const GLsizei first = byteOffset / bytesPerElem; + const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count); + if (!checked_byteCount.isValid()) { ErrorInvalidValue("%s: overflow in byteCount", info); return false; @@ -292,12 +423,6 @@ WebGLContext::DrawElements_check(GLsizei count, GLenum type, return false; } - if (!DrawInstanced_check(info)) { - return false; - } - - BindFakeBlackTextures(); - return true; } @@ -305,18 +430,21 @@ void WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type, WebGLintptr byteOffset) { + const char funcName[] = "drawElements"; if (IsContextLost()) return; - if (!ValidateDrawModeEnum(mode, "drawElements: mode")) + if (!ValidateDrawModeEnum(mode, funcName)) + return; + + bool error; + ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); + if (error) return; GLuint upperBound = 0; - if (!DrawElements_check(count, type, byteOffset, 1, "drawElements", - &upperBound)) - { + if (!DrawElements_check(count, type, byteOffset, 1, funcName, &upperBound)) return; - } RunContextLossTimer(); @@ -332,25 +460,31 @@ WebGLContext::DrawElements(GLenum mode, GLsizei count, GLenum type, } } - Draw_cleanup(); + Draw_cleanup(funcName); } void WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, WebGLintptr byteOffset, GLsizei primcount) { + const char funcName[] = "drawElementsInstanced"; if (IsContextLost()) return; - if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode")) + if (!ValidateDrawModeEnum(mode, funcName)) + return; + + bool error; + ScopedResolveTexturesForDraw scopedResolve(this, funcName, &error); + if (error) return; GLuint upperBound = 0; - if (!DrawElements_check(count, type, byteOffset, primcount, - "drawElementsInstanced", &upperBound)) - { + if (!DrawElements_check(count, type, byteOffset, primcount, funcName, &upperBound)) + return; + + if (!DrawInstanced_check(funcName)) return; - } RunContextLossTimer(); @@ -361,13 +495,12 @@ WebGLContext::DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, primcount); } - Draw_cleanup(); + Draw_cleanup(funcName); } -void WebGLContext::Draw_cleanup() +void WebGLContext::Draw_cleanup(const char* funcName) { UndoFakeVertexAttrib0(); - UnbindFakeBlackTextures(); if (!mBoundDrawFramebuffer) { Invalidate(); @@ -386,17 +519,29 @@ void WebGLContext::Draw_cleanup() } } - // Let's check the viewport - const WebGLRectangleObject* rect = CurValidDrawFBRectObject(); - if (rect) { - if (mViewportWidth > rect->Width() || - mViewportHeight > rect->Height()) - { - if (!mAlreadyWarnedAboutViewportLargerThanDest) { - GenerateWarning("Drawing to a destination rect smaller than the viewport rect. " - "(This warning will only be given once)"); - mAlreadyWarnedAboutViewportLargerThanDest = true; - } + // Let's check for a really common error: Viewport is larger than the actual + // destination framebuffer. + uint32_t destWidth = mViewportWidth; + uint32_t destHeight = mViewportHeight; + + if (mBoundDrawFramebuffer) { + const auto& fba = mBoundDrawFramebuffer->ColorAttachment(0); + if (fba.IsDefined()) { + fba.Size(&destWidth, &destHeight); + } + } else { + destWidth = mWidth; + destHeight = mHeight; + } + + if (mViewportWidth > int32_t(destWidth) || + mViewportHeight > int32_t(destHeight)) + { + if (!mAlreadyWarnedAboutViewportLargerThanDest) { + GenerateWarning("%s: Drawing to a destination rect smaller than the viewport" + " rect. (This warning will only be given once)", + funcName); + mAlreadyWarnedAboutViewportLargerThanDest = true; } } } @@ -646,145 +791,67 @@ WebGLContext::UndoFakeVertexAttrib0() gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->mGLName : 0); } -WebGLContextFakeBlackStatus -WebGLContext::ResolvedFakeBlackStatus() +static GLuint +CreateGLTexture(gl::GLContext* gl) { - // handle this case first, it's the generic case - if (MOZ_LIKELY(mFakeBlackStatus == WebGLContextFakeBlackStatus::NotNeeded)) - return mFakeBlackStatus; - - if (mFakeBlackStatus == WebGLContextFakeBlackStatus::Needed) - return mFakeBlackStatus; - - for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) { - if ((mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) || - (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded)) - { - mFakeBlackStatus = WebGLContextFakeBlackStatus::Needed; - return mFakeBlackStatus; - } - } - - // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, - // that means that we do NOT need it. - mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded; - return mFakeBlackStatus; + MOZ_ASSERT(gl->IsCurrent()); + GLuint ret = 0; + gl->fGenTextures(1, &ret); + return ret; } -void -WebGLContext::BindFakeBlackTexturesHelper( - GLenum target, - const nsTArray > & boundTexturesArray, - UniquePtr & opaqueTextureScopedPtr, - UniquePtr & transparentTextureScopedPtr) -{ - for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) { - if (!boundTexturesArray[i]) { - continue; - } - - WebGLTextureFakeBlackStatus s = boundTexturesArray[i]->ResolvedFakeBlackStatus(); - MOZ_ASSERT(s != WebGLTextureFakeBlackStatus::Unknown); - - if (MOZ_LIKELY(s == WebGLTextureFakeBlackStatus::NotNeeded)) { - continue; - } - - bool alpha = s == WebGLTextureFakeBlackStatus::UninitializedImageData && - FormatHasAlpha(boundTexturesArray[i]->ImageInfoBase().EffectiveInternalFormat()); - UniquePtr& - blackTexturePtr = alpha - ? transparentTextureScopedPtr - : opaqueTextureScopedPtr; - - if (!blackTexturePtr) { - GLenum format = alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB; - blackTexturePtr = MakeUnique(gl, target, format); - } - - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i); - gl->fBindTexture(target, - blackTexturePtr->GLName()); - } -} - -void -WebGLContext::BindFakeBlackTextures() -{ - // this is the generic case: try to return early - if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded)) - return; - - BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_2D, - mBound2DTextures, - mBlackOpaqueTexture2D, - mBlackTransparentTexture2D); - BindFakeBlackTexturesHelper(LOCAL_GL_TEXTURE_CUBE_MAP, - mBoundCubeMapTextures, - mBlackOpaqueTextureCubeMap, - mBlackTransparentTextureCubeMap); -} - -void -WebGLContext::UnbindFakeBlackTextures() -{ - // this is the generic case: try to return early - if (MOZ_LIKELY(ResolvedFakeBlackStatus() == WebGLContextFakeBlackStatus::NotNeeded)) - return; - - for (int32_t i = 0; i < mGLMaxTextureUnits; ++i) { - if (mBound2DTextures[i] && mBound2DTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) { - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i); - gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->mGLName); - } - if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->ResolvedFakeBlackStatus() != WebGLTextureFakeBlackStatus::NotNeeded) { - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i); - gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->mGLName); - } - } - - gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture); -} - -WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, GLenum format) +WebGLContext::FakeBlackTexture::FakeBlackTexture(gl::GLContext* gl, TexTarget target, + FakeBlackType type) : mGL(gl) - , mGLName(0) + , mGLName(CreateGLTexture(gl)) { - MOZ_ASSERT(format == LOCAL_GL_RGB || format == LOCAL_GL_RGBA); + GLenum texFormat; + switch (type) { + case FakeBlackType::RGBA0000: + texFormat = LOCAL_GL_RGBA; + break; - mGL->MakeCurrent(); - GLuint formerBinding = 0; - gl->GetUIntegerv(target == LOCAL_GL_TEXTURE_2D - ? LOCAL_GL_TEXTURE_BINDING_2D - : LOCAL_GL_TEXTURE_BINDING_CUBE_MAP, - &formerBinding); - gl->fGenTextures(1, &mGLName); - gl->fBindTexture(target.get(), mGLName); + case FakeBlackType::RGBA0001: + texFormat = LOCAL_GL_RGB; + break; - // we allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) - // to minimize the risk of running into a driver bug in texImage2D, as it is - // a bit unusual maybe to create 1x1 textures, and the stack may not have the alignment - // that texImage2D expects. - UniquePtr zeros((uint8_t*)moz_xcalloc(1, 16)); - if (target == LOCAL_GL_TEXTURE_2D) { - gl->fTexImage2D(target.get(), 0, format, 1, 1, - 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get()); - } else { - for (GLuint i = 0; i < 6; ++i) { - gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, 1, 1, - 0, format, LOCAL_GL_UNSIGNED_BYTE, zeros.get()); - } - } + default: + MOZ_CRASH("bad type"); + } - gl->fBindTexture(target.get(), formerBinding); + gl::ScopedBindTexture scopedBind(mGL, mGLName, target.get()); + + mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); + mGL->fTexParameteri(target.get(), LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); + + // We allocate our zeros on the heap, and we overallocate (16 bytes instead of 4) to + // minimize the risk of running into a driver bug in texImage2D, as it is a bit + // unusual maybe to create 1x1 textures, and the stack may not have the alignment that + // TexImage2D expects. + + const webgl::DriverUnpackInfo dui = {texFormat, texFormat, LOCAL_GL_UNSIGNED_BYTE}; + UniqueBuffer zeros = moz_xcalloc(1, 16); // Infallible allocation. + + if (target == LOCAL_GL_TEXTURE_CUBE_MAP) { + for (int i = 0; i < 6; ++i) { + const TexImageTarget curTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + const GLenum error = DoTexImage(mGL, curTarget.get(), 0, &dui, 1, 1, 1, + zeros.get()); + if (error) + MOZ_CRASH("Unexpected error during FakeBlack creation."); + } + } else { + const GLenum error = DoTexImage(mGL, target.get(), 0, &dui, 1, 1, 1, + zeros.get()); + if (error) + MOZ_CRASH("Unexpected error during FakeBlack creation."); + } } WebGLContext::FakeBlackTexture::~FakeBlackTexture() { - if (mGL) { - mGL->MakeCurrent(); - mGL->fDeleteTextures(1, &mGLName); - } + mGL->MakeCurrent(); + mGL->fDeleteTextures(1, &mGLName); } } // namespace mozilla diff --git a/dom/canvas/WebGLContextExtensions.cpp b/dom/canvas/WebGLContextExtensions.cpp index 39b3ebd647..86708995b0 100644 --- a/dom/canvas/WebGLContextExtensions.cpp +++ b/dom/canvas/WebGLContextExtensions.cpp @@ -106,46 +106,20 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const if (mDisableExtensions) return false; - // In alphabetical order + // Extensions for both WebGL 1 and 2. switch (ext) { - // ANGLE_ - case WebGLExtensionID::ANGLE_instanced_arrays: - return WebGLExtensionInstancedArrays::IsSupported(this); - + // In alphabetical order // EXT_ - case WebGLExtensionID::EXT_blend_minmax: - return WebGLExtensionBlendMinMax::IsSupported(this); case WebGLExtensionID::EXT_color_buffer_half_float: return WebGLExtensionColorBufferHalfFloat::IsSupported(this); - case WebGLExtensionID::EXT_frag_depth: - return WebGLExtensionFragDepth::IsSupported(this); - case WebGLExtensionID::EXT_shader_texture_lod: - return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod); - case WebGLExtensionID::EXT_sRGB: - return WebGLExtensionSRGB::IsSupported(this); case WebGLExtensionID::EXT_texture_filter_anisotropic: return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic); // OES_ - case WebGLExtensionID::OES_element_index_uint: - return gl->IsSupported(gl::GLFeature::element_index_uint); - case WebGLExtensionID::OES_standard_derivatives: - return gl->IsSupported(gl::GLFeature::standard_derivatives); - case WebGLExtensionID::OES_texture_float: - return gl->IsSupported(gl::GLFeature::texture_float); case WebGLExtensionID::OES_texture_float_linear: return gl->IsSupported(gl::GLFeature::texture_float_linear); - case WebGLExtensionID::OES_texture_half_float: - // If we have Feature::texture_half_float, we must not be on ES2 - // and need to translate HALF_FLOAT_OES -> HALF_FLOAT. We do that - // right before making the relevant calls. - return gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float) || - gl->IsSupported(gl::GLFeature::texture_half_float); - case WebGLExtensionID::OES_texture_half_float_linear: return gl->IsSupported(gl::GLFeature::texture_half_float_linear); - case WebGLExtensionID::OES_vertex_array_object: - return true; // WEBGL_ case WebGLExtensionID::WEBGL_color_buffer_float: @@ -167,15 +141,6 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const case WebGLExtensionID::WEBGL_debug_renderer_info: return Preferences::GetBool("webgl.enable-debug-renderer-info", false); - case WebGLExtensionID::WEBGL_depth_texture: - // WEBGL_depth_texture supports DEPTH_STENCIL textures - if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil)) - return false; - - return gl->IsSupported(gl::GLFeature::depth_texture) || - gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture); - case WebGLExtensionID::WEBGL_draw_buffers: - return WebGLExtensionDrawBuffers::IsSupported(this); case WebGLExtensionID::WEBGL_lose_context: // We always support this extension. return true; @@ -185,14 +150,61 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const break; } - if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) { + if (!IsWebGL2()) { + // WebGL1-only extensions switch (ext) { - case WebGLExtensionID::EXT_disjoint_timer_query: - return WebGLExtensionDisjointTimerQuery::IsSupported(this); + // ANGLE_ + case WebGLExtensionID::ANGLE_instanced_arrays: + return WebGLExtensionInstancedArrays::IsSupported(this); + + // EXT_ + case WebGLExtensionID::EXT_blend_minmax: + return WebGLExtensionBlendMinMax::IsSupported(this); + case WebGLExtensionID::EXT_frag_depth: + return WebGLExtensionFragDepth::IsSupported(this); + case WebGLExtensionID::EXT_shader_texture_lod: + return gl->IsExtensionSupported(gl::GLContext::EXT_shader_texture_lod); + case WebGLExtensionID::EXT_sRGB: + return WebGLExtensionSRGB::IsSupported(this); + + // OES_ + case WebGLExtensionID::OES_element_index_uint: + return gl->IsSupported(gl::GLFeature::element_index_uint); + case WebGLExtensionID::OES_standard_derivatives: + return gl->IsSupported(gl::GLFeature::standard_derivatives); + case WebGLExtensionID::OES_texture_float: + return WebGLExtensionTextureFloat::IsSupported(this); + case WebGLExtensionID::OES_texture_half_float: + return WebGLExtensionTextureHalfFloat::IsSupported(this); + + case WebGLExtensionID::OES_vertex_array_object: + return true; + + // WEBGL_ + case WebGLExtensionID::WEBGL_depth_texture: + // WEBGL_depth_texture supports DEPTH_STENCIL textures + if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil)) + return false; + + return gl->IsSupported(gl::GLFeature::depth_texture) || + gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture); + case WebGLExtensionID::WEBGL_draw_buffers: + return WebGLExtensionDrawBuffers::IsSupported(this); default: // For warnings-as-errors. break; } + + if (gfxPrefs::WebGLDraftExtensionsEnabled()) { + switch (ext) { + case WebGLExtensionID::EXT_disjoint_timer_query: + return WebGLExtensionDisjointTimerQuery::IsSupported(this); + + default: + // For warnings-as-errors. + break; + } + } } return false; diff --git a/dom/canvas/WebGLContextFramebufferOperations.cpp b/dom/canvas/WebGLContextFramebufferOperations.cpp index 0ad3838935..ea5a29ba93 100644 --- a/dom/canvas/WebGLContextFramebufferOperations.cpp +++ b/dom/canvas/WebGLContextFramebufferOperations.cpp @@ -137,71 +137,80 @@ WebGLContext::DepthMask(WebGLboolean b) void WebGLContext::DrawBuffers(const dom::Sequence& buffers) { + const char funcName[] = "drawBuffers"; if (IsContextLost()) return; - const size_t buffersLength = buffers.Length(); - - if (!buffersLength) { - return ErrorInvalidValue("drawBuffers: invalid (buffers must not be empty)"); - } - if (!mBoundDrawFramebuffer) { - // OK: we are rendering in the default framebuffer - - /* EXT_draw_buffers : - If the GL is bound to the default framebuffer, then must be 1 - and the constant must be BACK or NONE. When draw buffer zero is - BACK, color values are written into the sole buffer for single- - buffered contexts, or into the back buffer for double-buffered - contexts. If DrawBuffersEXT is supplied with a constant other than - BACK and NONE, the error INVALID_OPERATION is generated. - */ - if (buffersLength != 1) { - return ErrorInvalidValue("drawBuffers: invalid (main framebuffer: buffers.length must be 1)"); - } - - if (buffers[0] == LOCAL_GL_NONE || buffers[0] == LOCAL_GL_BACK) { - gl->Screen()->SetDrawBuffer(buffers[0]); + // GLES 3.0.4 p186: + // "If the GL is bound to the default framebuffer, then `n` must be 1 and the + // constant must be BACK or NONE. [...] If DrawBuffers is supplied with a + // constant other than BACK and NONE, or with a value of `n` other than 1, the + // error INVALID_OPERATION is generated." + if (buffers.Length() != 1) { + ErrorInvalidOperation("%s: For the default framebuffer, `buffers` must have a" + " length of 1.", + funcName); return; } - return ErrorInvalidOperation("drawBuffers: invalid operation (main framebuffer: buffers[0] must be GL_NONE or GL_BACK)"); + + switch (buffers[0]) { + case LOCAL_GL_NONE: + case LOCAL_GL_BACK: + break; + + default: + ErrorInvalidOperation("%s: For the default framebuffer, `buffers[0]` must be" + " BACK or NONE.", + funcName); + return; + } + + mDefaultFB_DrawBuffer0 = buffers[0]; + gl->Screen()->SetDrawBuffer(buffers[0]); + return; } - // OK: we are rendering in a framebuffer object + // Framebuffer object (not default framebuffer) - if (buffersLength > size_t(mGLMaxDrawBuffers)) { - /* EXT_draw_buffers : - The maximum number of draw buffers is implementation-dependent. The - number of draw buffers supported can be queried by calling - GetIntegerv with the symbolic constant MAX_DRAW_BUFFERS_EXT. An - INVALID_VALUE error is generated if is greater than - MAX_DRAW_BUFFERS_EXT. - */ - return ErrorInvalidValue("drawBuffers: invalid (buffers.length > GL_MAX_DRAW_BUFFERS)"); + if (buffers.Length() > mImplMaxDrawBuffers) { + // "An INVALID_VALUE error is generated if `n` is greater than MAX_DRAW_BUFFERS." + ErrorInvalidValue("%s: `buffers` must have a length <= MAX_DRAW_BUFFERS.", + funcName); + return; } - for (uint32_t i = 0; i < buffersLength; i++) - { - /* EXT_draw_buffers : - If the GL is bound to a draw framebuffer object, the th buffer listed - in must be COLOR_ATTACHMENT_EXT or NONE. Specifying a - buffer out of order, BACK, or COLOR_ATTACHMENT_EXT where is - greater than or equal to the value of MAX_COLOR_ATTACHMENTS_EXT, - will generate the error INVALID_OPERATION. - */ - /* WEBGL_draw_buffers : - The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter. - */ + for (size_t i = 0; i < buffers.Length(); i++) { + // "If the GL is bound to a draw framebuffer object, the `i`th buffer listed in + // bufs must be COLOR_ATTACHMENTi or NONE. Specifying a buffer out of order, + // BACK, or COLOR_ATTACHMENTm where `m` is greater than or equal to the value of + // MAX_COLOR_ATTACHMENTS, will generate the error INVALID_OPERATION. + + // WEBGL_draw_buffers: + // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or + // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." + // This means that if buffers.Length() isn't larger than MaxDrawBuffers, it won't + // be larger than MaxColorAttachments. if (buffers[i] != LOCAL_GL_NONE && - buffers[i] != GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + i)) { - return ErrorInvalidOperation("drawBuffers: invalid operation (buffers[i] must be GL_NONE or GL_COLOR_ATTACHMENTi)"); + buffers[i] != LOCAL_GL_COLOR_ATTACHMENT0 + i) + { + ErrorInvalidOperation("%s: `buffers[i]` must be NONE or COLOR_ATTACHMENTi.", + funcName); + return; } } MakeContextCurrent(); - gl->fDrawBuffers(buffersLength, buffers.Elements()); + const GLenum* ptr = nullptr; + if (buffers.Length()) { + ptr = buffers.Elements(); + } + + gl->fDrawBuffers(buffers.Length(), ptr); + + const auto end = ptr + buffers.Length(); + mBoundDrawFramebuffer->mDrawBuffers.assign(ptr, end); } void diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index 524040c298..dd7f8ca992 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -60,37 +60,6 @@ using namespace mozilla::dom; using namespace mozilla::gfx; using namespace mozilla::gl; -static const WebGLRectangleObject* -CurValidFBRectObject(const WebGLContext* webgl, - const WebGLFramebuffer* boundFB) -{ - const WebGLRectangleObject* rect = nullptr; - - if (boundFB) { - // We don't really need to ask the driver. - // Use 'precheck' to just check that our internal state looks good. - FBStatus precheckStatus = boundFB->PrecheckFramebufferStatus(); - if (precheckStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE) - rect = &boundFB->RectangleObject(); - } else { - rect = static_cast(webgl); - } - - return rect; -} - -const WebGLRectangleObject* -WebGLContext::CurValidDrawFBRectObject() const -{ - return CurValidFBRectObject(this, mBoundDrawFramebuffer); -} - -const WebGLRectangleObject* -WebGLContext::CurValidReadFBRectObject() const -{ - return CurValidFBRectObject(this, mBoundReadFramebuffer); -} - // // WebGL API // @@ -424,16 +393,15 @@ WebGLContext::DeleteTexture(WebGLTexture* tex) if (mBoundReadFramebuffer) mBoundReadFramebuffer->DetachTexture(tex); - tex->InvalidateStatusOfAttachedFBs(); - GLuint activeTexture = mActiveTexture; for (int32_t i = 0; i < mGLMaxTextureUnits; i++) { - if ((mBound2DTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_2D) || - (mBoundCubeMapTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) || - (mBound3DTextures[i] == tex && tex->Target() == LOCAL_GL_TEXTURE_3D)) + if (mBound2DTextures[i] == tex || + mBoundCubeMapTextures[i] == tex || + mBound3DTextures[i] == tex || + mBound2DArrayTextures[i] == tex) { ActiveTexture(LOCAL_GL_TEXTURE0 + i); - BindTexture(tex->Target(), nullptr); + BindTexture(tex->Target().get(), nullptr); } } ActiveTexture(LOCAL_GL_TEXTURE0 + activeTexture); @@ -726,10 +694,12 @@ WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx, GLenum pname, ErrorResult& rv) { + const char funcName[] = "getFramebufferAttachmentParameter"; + if (IsContextLost()) return JS::NullValue(); - if (!ValidateFramebufferTarget(target, "getFramebufferAttachmentParameter")) + if (!ValidateFramebufferTarget(target, funcName)) return JS::NullValue(); WebGLFramebuffer* fb; @@ -747,189 +717,73 @@ WebGLContext::GetFramebufferAttachmentParameter(JSContext* cx, MOZ_CRASH("Bad target."); } - if (!fb) { - ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot query" - " framebuffer 0."); + if (fb) + return fb->GetAttachmentParameter(funcName, cx, target, attachment, pname, &rv); + + //////////////////////////////////// + + if (!IsWebGL2()) { + ErrorInvalidOperation("%s: Querying against the default framebuffer is not" + " allowed in WebGL 1.", + funcName); return JS::NullValue(); } - if (!ValidateFramebufferAttachment(fb, attachment, - "getFramebufferAttachmentParameter")) - { + switch (attachment) { + case LOCAL_GL_COLOR: + case LOCAL_GL_DEPTH: + case LOCAL_GL_STENCIL: + break; + + default: + ErrorInvalidEnum("%s: For the default framebuffer, can only query COLOR, DEPTH," + " or STENCIL.", + funcName); return JS::NullValue(); } - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers) && - attachment >= LOCAL_GL_COLOR_ATTACHMENT0 && - attachment <= LOCAL_GL_COLOR_ATTACHMENT15) - { - fb->EnsureColorAttachPoints(attachment - LOCAL_GL_COLOR_ATTACHMENT0); - } - - MakeContextCurrent(); - - const WebGLFBAttachPoint& fba = fb->GetAttachPoint(attachment); - - if (fba.Renderbuffer()) { - switch (pname) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: - if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) { - const GLenum internalFormat = fba.Renderbuffer()->InternalFormat(); - return (internalFormat == LOCAL_GL_SRGB_EXT || - internalFormat == LOCAL_GL_SRGB_ALPHA_EXT || - internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT) ? - JS::NumberValue(uint32_t(LOCAL_GL_SRGB_EXT)) : - JS::NumberValue(uint32_t(LOCAL_GL_LINEAR)); - } - break; - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return JS::NumberValue(uint32_t(LOCAL_GL_RENDERBUFFER)); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - return WebGLObjectAsJSValue(cx, fba.Renderbuffer(), rv); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: { - if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) && - !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) - { - break; - } - - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - ErrorInvalidOperation("getFramebufferAttachmentParameter: Cannot get component" - " type of a depth-stencil attachment."); - return JS::NullValue(); - } - - if (!fba.IsComplete()) - return JS::NumberValue(uint32_t(LOCAL_GL_NONE)); - - uint32_t ret = LOCAL_GL_NONE; - switch (fba.Renderbuffer()->InternalFormat()) { - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGB5_A1: - case LOCAL_GL_RGB565: - case LOCAL_GL_SRGB8_ALPHA8: - ret = LOCAL_GL_UNSIGNED_NORMALIZED; - break; - case LOCAL_GL_RGB16F: - case LOCAL_GL_RGBA16F: - case LOCAL_GL_RGB32F: - case LOCAL_GL_RGBA32F: - ret = LOCAL_GL_FLOAT; - break; - case LOCAL_GL_DEPTH_COMPONENT16: - case LOCAL_GL_STENCIL_INDEX8: - ret = LOCAL_GL_UNSIGNED_INT; - break; - default: - MOZ_ASSERT(false, "Unhandled RB component type."); - break; - } - return JS::NumberValue(uint32_t(ret)); - } - } - - ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname); - return JS::NullValue(); - } else if (fba.Texture()) { - switch (pname) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: - if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) { - const TexInternalFormat effectiveInternalFormat = - fba.Texture()->ImageInfoBase().EffectiveInternalFormat(); - - if (effectiveInternalFormat == LOCAL_GL_NONE) { - ErrorInvalidOperation("getFramebufferAttachmentParameter: " - "texture contains no data"); - return JS::NullValue(); - } - - TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE; - TexType type = LOCAL_GL_NONE; - UnsizedInternalFormatAndTypeFromEffectiveInternalFormat( - effectiveInternalFormat, &unsizedinternalformat, &type); - MOZ_ASSERT(unsizedinternalformat != LOCAL_GL_NONE); - const bool srgb = unsizedinternalformat == LOCAL_GL_SRGB || - unsizedinternalformat == LOCAL_GL_SRGB_ALPHA; - return srgb ? JS::NumberValue(uint32_t(LOCAL_GL_SRGB)) - : JS::NumberValue(uint32_t(LOCAL_GL_LINEAR)); - } - break; - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return JS::NumberValue(uint32_t(LOCAL_GL_TEXTURE)); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - return WebGLObjectAsJSValue(cx, fba.Texture(), rv); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: - return JS::Int32Value(fba.MipLevel()); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: { - GLenum face = fba.ImageTarget().get(); - if (face == LOCAL_GL_TEXTURE_2D) - face = 0; - return JS::Int32Value(face); - } - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: { - if (!IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float) && - !IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) - { - break; - } - - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - ErrorInvalidOperation("getFramebufferAttachmentParameter: cannot component" - " type of depth-stencil attachments."); - return JS::NullValue(); - } - - if (!fba.IsComplete()) - return JS::NumberValue(uint32_t(LOCAL_GL_NONE)); - - TexInternalFormat effectiveInternalFormat = - fba.Texture()->ImageInfoAt(fba.ImageTarget(), fba.MipLevel()).EffectiveInternalFormat(); - TexType type = TypeFromInternalFormat(effectiveInternalFormat); - GLenum ret = LOCAL_GL_NONE; - switch (type.get()) { - case LOCAL_GL_UNSIGNED_BYTE: - case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: - case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: - case LOCAL_GL_UNSIGNED_SHORT_5_6_5: - ret = LOCAL_GL_UNSIGNED_NORMALIZED; - break; - case LOCAL_GL_FLOAT: - case LOCAL_GL_HALF_FLOAT: - ret = LOCAL_GL_FLOAT; - break; - case LOCAL_GL_UNSIGNED_SHORT: - case LOCAL_GL_UNSIGNED_INT: - ret = LOCAL_GL_UNSIGNED_INT; - break; - default: - MOZ_ASSERT(false, "Unhandled RB component type."); - break; - } - return JS::NumberValue(uint32_t(ret)); - } - } - - ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname); - return JS::NullValue(); - } else { - switch (pname) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return JS::NumberValue(uint32_t(LOCAL_GL_NONE)); - - default: - ErrorInvalidEnumInfo("getFramebufferAttachmentParameter: pname", pname); - return JS::NullValue(); - } + switch (pname) { + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return JS::NullValue(); + + //////////////// + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: + if (attachment == LOCAL_GL_COLOR) + return JS::NumberValue(8); + return JS::NumberValue(0); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: + if (attachment == LOCAL_GL_COLOR) + return JS::NumberValue(mOptions.alpha ? 8 : 0); + return JS::NumberValue(0); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: + if (attachment == LOCAL_GL_DEPTH) + return JS::NumberValue(mOptions.depth ? 24 : 0); + return JS::NumberValue(0); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: + if (attachment == LOCAL_GL_STENCIL) + return JS::NumberValue(mOptions.stencil ? 8 : 0); + return JS::NumberValue(0); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: + if (attachment == LOCAL_GL_STENCIL) + return JS::NumberValue(LOCAL_GL_UNSIGNED_INT); + else + return JS::NumberValue(LOCAL_GL_UNSIGNED_NORMALIZED); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: + return JS::NumberValue(LOCAL_GL_LINEAR); } + ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname); return JS::NullValue(); } @@ -960,15 +814,12 @@ WebGLContext::GetRenderbufferParameter(GLenum target, GLenum pname) case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: + case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: { // RB emulation means we have to ask the RB itself. GLint i = mBoundRenderbuffer->GetRenderbufferParameter(target, pname); return JS::Int32Value(i); } - case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: - { - return JS::NumberValue(mBoundRenderbuffer->InternalFormat()); - } default: ErrorInvalidEnumInfo("getRenderbufferParameter: parameter", pname); } @@ -1108,13 +959,17 @@ WebGLContext::Hint(GLenum target, GLenum mode) bool isValid = false; switch (target) { - case LOCAL_GL_GENERATE_MIPMAP_HINT: + case LOCAL_GL_GENERATE_MIPMAP_HINT: + isValid = true; + break; + + case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: + if (IsWebGL2() || + IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) + { isValid = true; - break; - case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: - if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) - isValid = true; - break; + } + break; } if (!isValid) @@ -1220,43 +1075,112 @@ WebGLContext::PixelStorei(GLenum pname, GLint param) if (IsContextLost()) return; - switch (pname) { - case UNPACK_FLIP_Y_WEBGL: - mPixelStoreFlipY = (param != 0); + if (IsWebGL2()) { + uint32_t* pValueSlot = nullptr; + switch (pname) { + case LOCAL_GL_UNPACK_IMAGE_HEIGHT: + pValueSlot = &mPixelStore_UnpackImageHeight; break; - case UNPACK_PREMULTIPLY_ALPHA_WEBGL: - mPixelStorePremultiplyAlpha = (param != 0); + + case LOCAL_GL_UNPACK_SKIP_IMAGES: + pValueSlot = &mPixelStore_UnpackSkipImages; break; - case UNPACK_COLORSPACE_CONVERSION_WEBGL: - if (param == LOCAL_GL_NONE || param == BROWSER_DEFAULT_WEBGL) - mPixelStoreColorspaceConversion = param; - else - return ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", param); + + case LOCAL_GL_UNPACK_ROW_LENGTH: + pValueSlot = &mPixelStore_UnpackRowLength; break; - case LOCAL_GL_PACK_ALIGNMENT: - case LOCAL_GL_UNPACK_ALIGNMENT: - if (param != 1 && - param != 2 && - param != 4 && - param != 8) - return ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value"); - if (pname == LOCAL_GL_PACK_ALIGNMENT) - mPixelStorePackAlignment = param; - else if (pname == LOCAL_GL_UNPACK_ALIGNMENT) - mPixelStoreUnpackAlignment = param; + + case LOCAL_GL_UNPACK_SKIP_ROWS: + pValueSlot = &mPixelStore_UnpackSkipRows; + break; + + case LOCAL_GL_UNPACK_SKIP_PIXELS: + pValueSlot = &mPixelStore_UnpackSkipPixels; + break; + + case LOCAL_GL_PACK_ROW_LENGTH: + pValueSlot = &mPixelStore_PackRowLength; + break; + + case LOCAL_GL_PACK_SKIP_ROWS: + pValueSlot = &mPixelStore_PackSkipRows; + break; + + case LOCAL_GL_PACK_SKIP_PIXELS: + pValueSlot = &mPixelStore_PackSkipPixels; + break; + } + + if (pValueSlot) { + if (param < 0) { + ErrorInvalidValue("pixelStorei: param must be >= 0."); + return; + } + MakeContextCurrent(); gl->fPixelStorei(pname, param); - break; - default: - return ErrorInvalidEnumInfo("pixelStorei: parameter", pname); + *pValueSlot = param; + return; + } } + + switch (pname) { + case UNPACK_FLIP_Y_WEBGL: + mPixelStore_FlipY = bool(param); + return; + + case UNPACK_PREMULTIPLY_ALPHA_WEBGL: + mPixelStore_PremultiplyAlpha = bool(param); + return; + + case UNPACK_COLORSPACE_CONVERSION_WEBGL: + switch (param) { + case LOCAL_GL_NONE: + case BROWSER_DEFAULT_WEBGL: + mPixelStore_ColorspaceConversion = param; + return; + + default: + ErrorInvalidEnumInfo("pixelStorei: colorspace conversion parameter", + param); + return; + } + + case LOCAL_GL_PACK_ALIGNMENT: + case LOCAL_GL_UNPACK_ALIGNMENT: + switch (param) { + case 1: + case 2: + case 4: + case 8: + if (pname == LOCAL_GL_PACK_ALIGNMENT) + mPixelStore_PackAlignment = param; + else if (pname == LOCAL_GL_UNPACK_ALIGNMENT) + mPixelStore_UnpackAlignment = param; + + MakeContextCurrent(); + gl->fPixelStorei(pname, param); + return; + + default: + ErrorInvalidValue("pixelStorei: invalid pack/unpack alignment value"); + return; + } + + + + default: + break; + } + + ErrorInvalidEnumInfo("pixelStorei: parameter", pname); } // `width` in pixels. // `stride` in bytes. -static bool -SetFullAlpha(void* data, GLenum format, GLenum type, size_t width, - size_t height, size_t stride) +static void +SetFullAlpha(void* data, GLenum format, GLenum type, size_t width, size_t height, + size_t stride) { if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) { // Just memset the rows. @@ -1266,7 +1190,7 @@ SetFullAlpha(void* data, GLenum format, GLenum type, size_t width, row += stride; } - return true; + return; } if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) { @@ -1281,7 +1205,7 @@ SetFullAlpha(void* data, GLenum format, GLenum type, size_t width, } } - return true; + return; } if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) { @@ -1297,60 +1221,92 @@ SetFullAlpha(void* data, GLenum format, GLenum type, size_t width, } } - return true; - } - - MOZ_ASSERT(false, "Unhandled case, how'd we get here?"); - return false; -} - -static void -ReadPixelsAndConvert(gl::GLContext* gl, GLint x, GLint y, GLsizei width, GLsizei height, - GLenum readFormat, GLenum readType, size_t pixelStorePackAlignment, - GLenum destFormat, GLenum destType, void* destBytes) -{ - if (readFormat == destFormat && readType == destType) { - gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes); return; } - if (readFormat == LOCAL_GL_RGBA && - readType == LOCAL_GL_HALF_FLOAT && - destFormat == LOCAL_GL_RGBA && - destType == LOCAL_GL_FLOAT) + MOZ_CRASH("Unhandled case, how'd we get here?"); +} + +bool +WebGLContext::DoReadPixelsAndConvert(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum destFormat, GLenum destType, void* destBytes, + GLenum auxReadFormat, GLenum auxReadType) +{ + GLenum readFormat = destFormat; + GLenum readType = destType; + + if (gl->WorkAroundDriverBugs() && + gl->IsANGLE() && + readType == LOCAL_GL_FLOAT && + auxReadFormat == destFormat && + auxReadType == LOCAL_GL_HALF_FLOAT) { - size_t readBytesPerPixel = sizeof(uint16_t) * 4; - size_t destBytesPerPixel = sizeof(float) * 4; + readType = auxReadType; - size_t readBytesPerRow = readBytesPerPixel * width; + const auto readBytesPerPixel = webgl::BytesPerPixel({readFormat, readType}); + const auto destBytesPerPixel = webgl::BytesPerPixel({destFormat, destType}); - size_t readStride = RoundUpToMultipleOf(readBytesPerRow, pixelStorePackAlignment); - size_t destStride = RoundUpToMultipleOf(destBytesPerPixel * width, - pixelStorePackAlignment); + CheckedUint32 readOffset; + CheckedUint32 readStride; + const CheckedUint32 readSize = GetPackSize(width, height, readBytesPerPixel, + &readOffset, &readStride); - size_t bytesNeeded = ((height - 1) * readStride) + readBytesPerRow; - UniquePtr readBuffer(new uint8_t[bytesNeeded]); + CheckedUint32 destOffset; + CheckedUint32 destStride; + const CheckedUint32 destSize = GetPackSize(width, height, destBytesPerPixel, + &destOffset, &destStride); + if (!readSize.isValid() || !destSize.isValid()) { + ErrorOutOfMemory("readPixels: Overflow calculating sizes for conversion."); + return false; + } + + UniqueBuffer readBuffer = malloc(readSize.value()); + if (!readBuffer) { + ErrorOutOfMemory("readPixels: Failed to alloc temp buffer for conversion."); + return false; + } + + gl::GLContext::LocalErrorScope errorScope(*gl); gl->fReadPixels(x, y, width, height, readFormat, readType, readBuffer.get()); - size_t channelsPerRow = width * 4; - for (size_t j = 0; j < (size_t)height; j++) { - uint16_t* src = (uint16_t*)(readBuffer.get()) + j*readStride; - float* dst = (float*)(destBytes) + j*destStride; + const GLenum error = errorScope.GetError(); + if (error == LOCAL_GL_OUT_OF_MEMORY) { + ErrorOutOfMemory("readPixels: Driver ran out of memory."); + return false; + } - uint16_t* srcEnd = src + channelsPerRow; + if (error) { + MOZ_RELEASE_ASSERT(false, "Unexpected driver error."); + return false; + } + + size_t channelsPerRow = std::min(readStride.value() / sizeof(uint16_t), + destStride.value() / sizeof(float)); + + const uint8_t* srcRow = (uint8_t*)(readBuffer.get()) + readOffset.value(); + uint8_t* dstRow = (uint8_t*)(destBytes) + destOffset.value(); + + for (size_t j = 0; j < (size_t)height; j++) { + auto src = (const uint16_t*)srcRow; + auto dst = (float*)dstRow; + + const auto srcEnd = src + channelsPerRow; while (src != srcEnd) { *dst = unpackFromFloat16(*src); - ++src; ++dst; } + + srcRow += readStride.value(); + dstRow += destStride.value(); } - return; + return true; } - MOZ_CRASH("bad format/type"); + gl->fReadPixels(x, y, width, height, destFormat, destType, destBytes); + return true; } static bool @@ -1382,6 +1338,37 @@ IsFormatAndTypeUnpackable(GLenum format, GLenum type) } } +CheckedUint32 +WebGLContext::GetPackSize(uint32_t width, uint32_t height, uint8_t bytesPerPixel, + CheckedUint32* const out_startOffset, + CheckedUint32* const out_rowStride) +{ + if (!width || !height) { + *out_startOffset = 0; + *out_rowStride = 0; + return 0; + } + + const CheckedUint32 pixelsPerRow = (mPixelStore_PackRowLength ? mPixelStore_PackRowLength + : width); + const CheckedUint32 skipPixels = mPixelStore_PackSkipPixels; + const CheckedUint32 skipRows = mPixelStore_PackSkipRows; + const CheckedUint32 alignment = mPixelStore_PackAlignment; + + // GLES 3.0.4, p116 (PACK_ functions like UNPACK_) + const auto totalBytesPerRow = bytesPerPixel * pixelsPerRow; + const auto rowStride = RoundUpToMultipleOf(totalBytesPerRow, alignment); + + const auto startOffset = rowStride * skipRows + bytesPerPixel * skipPixels; + const auto usedBytesPerRow = bytesPerPixel * width; + + const auto bytesNeeded = startOffset + rowStride * (height - 1) + usedBytesPerRow; + + *out_startOffset = startOffset; + *out_rowStride = rowStride; + return bytesNeeded; +} + // This function is temporary, and will be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=1176214 lands, which will // collapse the SharedArrayBufferView and ArrayBufferView into one. void @@ -1390,25 +1377,26 @@ ComputeLengthAndData(const dom::ArrayBufferViewOrSharedArrayBufferView& view, js::Scalar::Type* const out_type) { if (view.IsArrayBufferView()) { - const dom::ArrayBufferView& pixbuf = view.GetAsArrayBufferView(); - pixbuf.ComputeLengthAndData(); - *out_length = pixbuf.Length(); - *out_data = pixbuf.Data(); - *out_type = JS_GetArrayBufferViewType(pixbuf.Obj()); + const auto& view2 = view.GetAsArrayBufferView(); + view2.ComputeLengthAndData(); + + *out_length = view2.Length(); + *out_data = view2.Data(); + *out_type = JS_GetArrayBufferViewType(view2.Obj()); } else { - const dom::SharedArrayBufferView& pixbuf = view.GetAsSharedArrayBufferView(); - pixbuf.ComputeLengthAndData(); - *out_length = pixbuf.Length(); - *out_data = pixbuf.Data(); - *out_type = JS_GetSharedArrayBufferViewType(pixbuf.Obj()); + const auto& view2 = view.GetAsSharedArrayBufferView(); + view2.ComputeLengthAndData(); + *out_length = view2.Length(); + *out_data = view2.Data(); + *out_type = JS_GetSharedArrayBufferViewType(view2.Obj()); } } void -WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, - GLsizei height, GLenum format, - GLenum type, const dom::Nullable& pixels, - ErrorResult& rv) +WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, + const dom::Nullable& pixels, + ErrorResult& out_error) { if (IsContextLost()) return; @@ -1418,7 +1406,8 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, !nsContentUtils::IsCallerChrome()) { GenerateWarning("readPixels: Not allowed"); - return rv.Throw(NS_ERROR_DOM_SECURITY_ERR); + out_error.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; } if (width < 0 || height < 0) @@ -1430,10 +1419,6 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, if (!IsFormatAndTypeUnpackable(format, type)) return ErrorInvalidEnum("readPixels: Bad format or type."); - const WebGLRectangleObject* framebufferRect = CurValidReadFBRectObject(); - GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0; - GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0; - int channels = 0; // Check the format param @@ -1483,61 +1468,55 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, MOZ_CRASH("bad `type`"); } - const dom::ArrayBufferViewOrSharedArrayBufferView &view = pixels.Value(); + const auto& view = pixels.Value(); // Compute length and data. Don't reenter after this point, lest the // precomputed go out of sync with the instant length/data. - size_t dataByteLen; void* data; + size_t bytesAvailable; js::Scalar::Type dataType; - ComputeLengthAndData(view, &data, &dataByteLen, &dataType); + ComputeLengthAndData(view, &data, &bytesAvailable, &dataType); // Check the pixels param type if (dataType != requiredDataType) return ErrorInvalidOperation("readPixels: Mismatched type/pixels types"); - // Check the pixels param size - CheckedUint32 checked_neededByteLength = - GetImageSize(height, width, 1, bytesPerPixel, mPixelStorePackAlignment); + CheckedUint32 startOffset; + CheckedUint32 rowStride; + const auto bytesNeeded = GetPackSize(width, height, bytesPerPixel, &startOffset, + &rowStride); + if (!bytesNeeded.isValid()) { + ErrorInvalidOperation("readPixels: Integer overflow computing the needed buffer" + " size."); + return; + } - CheckedUint32 checked_plainRowSize = CheckedUint32(width) * bytesPerPixel; - - CheckedUint32 checked_alignedRowSize = - RoundedToNextMultipleOf(checked_plainRowSize, mPixelStorePackAlignment); - - if (!checked_neededByteLength.isValid()) - return ErrorInvalidOperation("readPixels: integer overflow computing the needed buffer size"); - - if (checked_neededByteLength.value() > dataByteLen) - return ErrorInvalidOperation("readPixels: buffer too small"); + if (bytesNeeded.value() > bytesAvailable) { + ErrorInvalidOperation("readPixels: buffer too small"); + return; + } if (!data) { ErrorOutOfMemory("readPixels: buffer storage is null. Did we run out of memory?"); - return rv.Throw(NS_ERROR_OUT_OF_MEMORY); + out_error.Throw(NS_ERROR_OUT_OF_MEMORY); + return; } MakeContextCurrent(); - bool isSourceTypeFloat; - if (mBoundReadFramebuffer) { - TexInternalFormat srcFormat; - if (!mBoundReadFramebuffer->ValidateForRead("readPixels", &srcFormat)) - return; + const webgl::FormatUsageInfo* srcFormat; + uint32_t srcWidth; + uint32_t srcHeight; + if (!ValidateCurFBForRead("readPixels", &srcFormat, &srcWidth, &srcHeight)) + return; - MOZ_ASSERT(srcFormat != LOCAL_GL_NONE); - TexType texType = TypeFromInternalFormat(srcFormat); - isSourceTypeFloat = (texType == LOCAL_GL_FLOAT || - texType == LOCAL_GL_HALF_FLOAT); - } else { - ClearBackbufferIfNeeded(); - - isSourceTypeFloat = false; - } + auto srcType = srcFormat->format->componentType; + const bool isSrcTypeFloat = (srcType == webgl::ComponentType::Float); // Check the format and type params to assure they are an acceptable pair (as per spec) const GLenum mainReadFormat = LOCAL_GL_RGBA; - const GLenum mainReadType = isSourceTypeFloat ? LOCAL_GL_FLOAT - : LOCAL_GL_UNSIGNED_BYTE; + const GLenum mainReadType = isSrcTypeFloat ? LOCAL_GL_FLOAT + : LOCAL_GL_UNSIGNED_BYTE; GLenum auxReadFormat = mainReadFormat; GLenum auxReadType = mainReadType; @@ -1557,91 +1536,89 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, if (!isValid) return ErrorInvalidOperation("readPixels: Invalid format/type pair"); - GLenum readType = type; - if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) { - if (type == LOCAL_GL_FLOAT && - auxReadFormat == format && - auxReadType == LOCAL_GL_HALF_FLOAT) - { - readType = auxReadType; - } + // Now that the errors are out of the way, on to actually reading! + + uint32_t readX, readY; + uint32_t writeX, writeY; + uint32_t rwWidth, rwHeight; + Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth); + Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight); + + if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) { + DoReadPixelsAndConvert(x, y, width, height, format, type, data, auxReadFormat, + auxReadType); + return; } - // Now that the errors are out of the way, on to actually reading + // Read request contains out-of-bounds pixels. Unfortunately: + // GLES 3.0.4 p194 "Obtaining Pixels from the Framebuffer": + // "If any of these pixels lies outside of the window allocated to the current GL + // context, or outside of the image attached to the currently bound framebuffer + // object, then the values obtained for those pixels are undefined." - // If we won't be reading any pixels anyways, just skip the actual reading - if (width == 0 || height == 0) - return DummyFramebufferOperation("readPixels"); + // This is a slow-path, so warn people away! + GenerateWarning("readPixels: Out-of-bounds reads with readPixels are deprecated, and" + " may be slow."); - if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) { - // the easy case: we're not reading out-of-range pixels + // Currently, the spec dictates that we need to zero the out-of-bounds pixels. - // Effectively: gl->fReadPixels(x, y, width, height, format, type, dest); - ReadPixelsAndConvert(gl, x, y, width, height, format, readType, - mPixelStorePackAlignment, format, type, data); - } else { - // the rectangle doesn't fit entirely in the bound buffer. We then have to set to zero the part - // of the buffer that correspond to out-of-range pixels. We don't want to rely on system OpenGL - // to do that for us, because passing out of range parameters to a buggy OpenGL implementation - // could conceivably allow to read memory we shouldn't be allowed to read. So we manually initialize - // the buffer to zero and compute the parameters to pass to OpenGL. We have to use an intermediate buffer - // to accomodate the potentially different strides (widths). + // Ideally we could just ReadPixels into the buffer, then zero the undefined parts. + // However, we can't do this for *shared* ArrayBuffers, as they can have racey + // accesses from Workers. - // Zero the whole pixel dest area in the destination buffer. - memset(data, 0, checked_neededByteLength.value()); + // We can use a couple tricks to do this faster, but we shouldn't encourage this + // anyway. Why not just do it the really safe, dead-simple way, even if it is + // hilariously slow? - if ( x >= framebufferWidth - || x+width <= 0 - || y >= framebufferHeight - || y+height <= 0) - { - // we are completely outside of range, can exit now with buffer filled with zeros - return DummyFramebufferOperation("readPixels"); + //////////////////////////////////// + // Clear the targetted pixels to zero. + + if (mPixelStore_PackRowLength || + mPixelStore_PackSkipPixels || + mPixelStore_PackSkipRows) + { + // Targetted bytes might not be contiguous, so do it row-by-row. + uint8_t* row = (uint8_t*)data + startOffset.value(); + const auto bytesPerRow = bytesPerPixel * width; + for (uint32_t j = 0; j < uint32_t(height); j++) { + std::memset(row, 0, bytesPerRow); + row += rowStride.value(); } + } else { + std::memset(data, 0, bytesNeeded.value()); + } - // compute the parameters of the subrect we're actually going to call glReadPixels on - GLint subrect_x = std::max(x, 0); - GLint subrect_end_x = std::min(x+width, framebufferWidth); - GLsizei subrect_width = subrect_end_x - subrect_x; + //////////////////////////////////// + // Read only the in-bounds pixels. - GLint subrect_y = std::max(y, 0); - GLint subrect_end_y = std::min(y+height, framebufferHeight); - GLsizei subrect_height = subrect_end_y - subrect_y; + if (!rwWidth || !rwHeight) { + // There aren't any, so we're 'done'. + DummyFramebufferOperation("readPixels"); + return; + } - if (subrect_width < 0 || subrect_height < 0 || - subrect_width > width || subrect_height > height) - return ErrorInvalidOperation("readPixels: integer overflow computing clipped rect size"); + if (IsWebGL2()) { + if (!mPixelStore_PackRowLength) { + gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, width); + } + gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels + writeX); + gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows + writeY); - // now we know that subrect_width is in the [0..width] interval, and same for heights. + DoReadPixelsAndConvert(readX, readY, rwWidth, rwHeight, format, type, data, + auxReadFormat, auxReadType); - // now, same computation as above to find the size of the intermediate buffer to allocate for the subrect - // no need to check again for integer overflow here, since we already know the sizes aren't greater than before - uint32_t subrect_plainRowSize = subrect_width * bytesPerPixel; - // There are checks above to ensure that this doesn't overflow. - uint32_t subrect_alignedRowSize = - RoundedToNextMultipleOf(subrect_plainRowSize, mPixelStorePackAlignment).value(); - uint32_t subrect_byteLength = (subrect_height-1)*subrect_alignedRowSize + subrect_plainRowSize; + gl->fPixelStorei(LOCAL_GL_PACK_ROW_LENGTH, mPixelStore_PackRowLength); + gl->fPixelStorei(LOCAL_GL_PACK_SKIP_PIXELS, mPixelStore_PackSkipPixels); + gl->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mPixelStore_PackSkipRows); + } else { + // I *did* say "hilariously slow". - // create subrect buffer, call glReadPixels, copy pixels into destination buffer, delete subrect buffer - auto subrect_data = MakeUniqueFallible(subrect_byteLength); - if (!subrect_data) - return ErrorOutOfMemory("readPixels: subrect_data"); - - // Effectively: gl->fReadPixels(subrect_x, subrect_y, subrect_width, - // subrect_height, format, type, subrect_data.get()); - ReadPixelsAndConvert(gl, subrect_x, subrect_y, subrect_width, subrect_height, - format, readType, mPixelStorePackAlignment, format, type, - subrect_data.get()); - - // notice that this for loop terminates because we already checked that subrect_height is at most height - for (GLint y_inside_subrect = 0; y_inside_subrect < subrect_height; ++y_inside_subrect) { - GLint subrect_x_in_dest_buffer = subrect_x - x; - GLint subrect_y_in_dest_buffer = subrect_y - y; - memcpy(static_cast(data) - + checked_alignedRowSize.value() * (subrect_y_in_dest_buffer + y_inside_subrect) - + bytesPerPixel * subrect_x_in_dest_buffer, // destination - subrect_data.get() + subrect_alignedRowSize * y_inside_subrect, // source - subrect_plainRowSize); // size + uint8_t* row = (uint8_t*)data + startOffset.value() + writeX * bytesPerPixel; + row += writeY * rowStride.value(); + for (uint32_t j = 0; j < rwHeight; j++) { + DoReadPixelsAndConvert(readX, readY+j, rwWidth, 1, format, type, row, + auxReadFormat, auxReadType); + row += rowStride.value(); } } @@ -1662,10 +1639,7 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, if (!needAlphaFilled) return; - size_t stride = checked_alignedRowSize.value(); // In bytes! - if (!SetFullAlpha(data, format, type, width, height, stride)) { - return rv.Throw(NS_ERROR_FAILURE); - } + SetFullAlpha(data, format, type, width, height, rowStride.value()); } void @@ -1697,91 +1671,32 @@ WebGLContext::RenderbufferStorage_base(const char* funcName, GLenum target, return; } - if (width > mGLMaxRenderbufferSize || height > mGLMaxRenderbufferSize) { + if (uint32_t(width) > mImplMaxRenderbufferSize || + uint32_t(height) > mImplMaxRenderbufferSize) + { ErrorInvalidValue("%s: Width or height exceeds maximum renderbuffer" " size.", funcName); return; } - // Convert DEPTH_STENCIL to sized type for testing - GLenum sizedInternalFormat = internalFormat; - if (sizedInternalFormat == LOCAL_GL_DEPTH_STENCIL) - sizedInternalFormat = LOCAL_GL_DEPTH24_STENCIL8; - - bool isFormatValid = false; - const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(sizedInternalFormat); - if (info) { - const webgl::FormatUsageInfo* usage = mFormatUsage->GetUsage(info); - isFormatValid = usage && usage->asRenderbuffer; - } - - if (!isFormatValid) { + const auto usage = mFormatUsage->GetRBUsage(internalFormat); + if (!usage) { ErrorInvalidEnumInfo("`internalFormat`", funcName, internalFormat); return; } - // certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL - GLenum internalFormatForGL = internalFormat; - - switch (internalFormat) { - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGB5_A1: - // 16-bit RGBA formats are not supported on desktop GL - if (!gl->IsGLES()) - internalFormatForGL = LOCAL_GL_RGBA8; - break; - - case LOCAL_GL_RGB565: - // the RGB565 format is not supported on desktop GL - if (!gl->IsGLES()) - internalFormatForGL = LOCAL_GL_RGB8; - break; - - case LOCAL_GL_DEPTH_COMPONENT16: - if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24)) - internalFormatForGL = LOCAL_GL_DEPTH_COMPONENT24; - else if (gl->IsExtensionSupported(gl::GLContext::OES_packed_depth_stencil)) - internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8; - break; - - case LOCAL_GL_DEPTH_STENCIL: - // We emulate this in WebGLRenderbuffer if we don't have the requisite extension. - internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8; - break; - - default: - break; - } - // Validation complete. MakeContextCurrent(); - bool willRealloc = samples != mBoundRenderbuffer->Samples() || - internalFormat != mBoundRenderbuffer->InternalFormat() || - width != mBoundRenderbuffer->Width() || - height != mBoundRenderbuffer->Height(); - - if (willRealloc) { - GetAndFlushUnderlyingGLErrors(); - mBoundRenderbuffer->RenderbufferStorage(samples, internalFormatForGL, - width, height); - GLenum error = GetAndFlushUnderlyingGLErrors(); - if (error) { - GenerateWarning("%s generated error %s", funcName, - ErrorName(error)); - return; - } - } else { - mBoundRenderbuffer->RenderbufferStorage(samples, internalFormatForGL, - width, height); + GetAndFlushUnderlyingGLErrors(); + mBoundRenderbuffer->RenderbufferStorage(samples, usage, width, height); + GLenum error = GetAndFlushUnderlyingGLErrors(); + if (error) { + GenerateWarning("%s generated error %s", funcName, + ErrorName(error)); + return; } - - mBoundRenderbuffer->SetSamples(samples); - mBoundRenderbuffer->SetInternalFormat(internalFormat); - mBoundRenderbuffer->SetInternalFormatForGL(internalFormatForGL); - mBoundRenderbuffer->setDimensions(width, height); - mBoundRenderbuffer->SetImageDataStatus(WebGLImageDataStatus::UninitializedImageData); } void @@ -1884,79 +1799,6 @@ WebGLContext::StencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum gl->fStencilOpSeparate(face, sfail, dpfail, dppass); } -nsresult -WebGLContext::SurfaceFromElementResultToImageSurface(nsLayoutUtils::SurfaceFromElementResult& res, - RefPtr& imageOut, - WebGLTexelFormat* format) -{ - *format = WebGLTexelFormat::None; - - if (!res.mSourceSurface) - return NS_OK; - RefPtr data = res.mSourceSurface->GetDataSurface(); - if (!data) { - // SurfaceFromElement lied! - return NS_OK; - } - - // We disallow loading cross-domain images and videos that have not been validated - // with CORS as WebGL textures. The reason for doing that is that timing - // attacks on WebGL shaders are able to retrieve approximations of the - // pixel values in WebGL textures; see bug 655987. - // - // To prevent a loophole where a Canvas2D would be used as a proxy to load - // cross-domain textures, we also disallow loading textures from write-only - // Canvas2D's. - - // part 1: check that the DOM element is same-origin, or has otherwise been - // validated for cross-domain use. - if (!res.mCORSUsed) { - bool subsumes; - nsresult rv = mCanvasElement->NodePrincipal()->Subsumes(res.mPrincipal, &subsumes); - if (NS_FAILED(rv) || !subsumes) { - GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. " - "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures"); - return NS_ERROR_DOM_SECURITY_ERR; - } - } - - // part 2: if the DOM element is write-only, it might contain - // cross-domain image data. - if (res.mIsWriteOnly) { - GenerateWarning("The canvas used as source for texImage2D here is tainted (write-only). It is forbidden " - "to load a WebGL texture from a tainted canvas. A Canvas becomes tainted for example " - "when a cross-domain image is drawn on it. " - "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures"); - return NS_ERROR_DOM_SECURITY_ERR; - } - - // End of security checks, now we should be safe regarding cross-domain images - // Notice that there is never a need to mark the WebGL canvas as write-only, since we reject write-only/cross-domain - // texture sources in the first place. - - switch (data->GetFormat()) { - case SurfaceFormat::B8G8R8A8: - *format = WebGLTexelFormat::BGRA8; // careful, our ARGB means BGRA - break; - case SurfaceFormat::B8G8R8X8: - *format = WebGLTexelFormat::BGRX8; // careful, our RGB24 is not tightly packed. Whence BGRX8. - break; - case SurfaceFormat::A8: - *format = WebGLTexelFormat::A8; - break; - case SurfaceFormat::R5G6B5_UINT16: - *format = WebGLTexelFormat::RGB565; - break; - default: - NS_ASSERTION(false, "Unsupported image format. Unimplemented."); - return NS_ERROR_NOT_IMPLEMENTED; - } - - imageOut = data; - - return NS_OK; -} - //////////////////////////////////////////////////////////////////////////////// // Uniform setters. diff --git a/dom/canvas/WebGLContextState.cpp b/dom/canvas/WebGLContextState.cpp index 952ae107c8..cee1b23ad4 100644 --- a/dom/canvas/WebGLContextState.cpp +++ b/dom/canvas/WebGLContextState.cpp @@ -77,7 +77,9 @@ WebGLContext::GetStencilBits(GLint* out_stencilBits) { *out_stencilBits = 0; if (mBoundDrawFramebuffer) { - if (mBoundDrawFramebuffer->HasDepthStencilConflict()) { + if (mBoundDrawFramebuffer->StencilAttachment().IsDefined() && + mBoundDrawFramebuffer->DepthStencilAttachment().IsDefined()) + { // Error, we don't know which stencil buffer's bits to use ErrorInvalidFramebufferOperation("getParameter: framebuffer has two stencil buffers bound"); return false; @@ -142,15 +144,15 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } } - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { if (pname == LOCAL_GL_MAX_COLOR_ATTACHMENTS) { - return JS::Int32Value(mGLMaxColorAttachments); + return JS::Int32Value(mImplMaxColorAttachments); } else if (pname == LOCAL_GL_MAX_DRAW_BUFFERS) { - return JS::Int32Value(mGLMaxDrawBuffers); + return JS::Int32Value(mImplMaxDrawBuffers); } else if (pname >= LOCAL_GL_DRAW_BUFFER0 && - pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mGLMaxDrawBuffers)) + pname < GLenum(LOCAL_GL_DRAW_BUFFER0 + mImplMaxDrawBuffers)) { GLint iv = 0; gl->fGetIntegerv(pname, &iv); @@ -166,7 +168,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } } - if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { if (pname == LOCAL_GL_VERTEX_ARRAY_BINDING) { WebGLVertexArray* vao = (mBoundVertexArray != mDefaultVertexArray) ? mBoundVertexArray.get() : nullptr; @@ -174,7 +176,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } } - if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) { if (pname == LOCAL_GL_TIMESTAMP_EXT) { GLuint64 iv = 0; gl->fGetInteger64v(pname, (GLint64*) &iv); @@ -235,7 +237,7 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) } } - if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) { + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) { if (pname == LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT) { GLint i = 0; gl->fGetIntegerv(pname, &i); @@ -374,13 +376,13 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) return JS::Int32Value(i); } case LOCAL_GL_MAX_TEXTURE_SIZE: - return JS::Int32Value(mGLMaxTextureSize); + return JS::Int32Value(mImplMaxTextureSize); case LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE: - return JS::Int32Value(mGLMaxCubeMapTextureSize); + return JS::Int32Value(mImplMaxCubeMapTextureSize); case LOCAL_GL_MAX_RENDERBUFFER_SIZE: - return JS::Int32Value(mGLMaxRenderbufferSize); + return JS::Int32Value(mImplMaxRenderbufferSize); case LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS: return JS::Int32Value(mGLMaxVertexUniformVectors); @@ -445,13 +447,13 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv) // bool, WebGL-specific case UNPACK_FLIP_Y_WEBGL: - return JS::BooleanValue(mPixelStoreFlipY); + return JS::BooleanValue(mPixelStore_FlipY); case UNPACK_PREMULTIPLY_ALPHA_WEBGL: - return JS::BooleanValue(mPixelStorePremultiplyAlpha); + return JS::BooleanValue(mPixelStore_PremultiplyAlpha); // uint, WebGL-specific case UNPACK_COLORSPACE_CONVERSION_WEBGL: - return JS::NumberValue(uint32_t(mPixelStoreColorspaceConversion)); + return JS::NumberValue(uint32_t(mPixelStore_ColorspaceConversion)); //////////////////////////////// // Complex values diff --git a/dom/canvas/WebGLContextTextures.cpp b/dom/canvas/WebGLContextTextures.cpp index af8eba5afa..d3eef6c9a8 100644 --- a/dom/canvas/WebGLContextTextures.cpp +++ b/dom/canvas/WebGLContextTextures.cpp @@ -52,31 +52,43 @@ namespace mozilla { static bool -IsValidTexTarget(WebGLContext* webgl, GLenum rawTexTarget, TexTarget* const out) +IsValidTexTarget(WebGLContext* webgl, uint8_t funcDims, GLenum rawTexTarget, + TexTarget* const out) { + uint8_t targetDims; + switch (rawTexTarget) { case LOCAL_GL_TEXTURE_2D: case LOCAL_GL_TEXTURE_CUBE_MAP: + targetDims = 2; break; case LOCAL_GL_TEXTURE_3D: + case LOCAL_GL_TEXTURE_2D_ARRAY: if (!webgl->IsWebGL2()) return false; + targetDims = 3; break; default: return false; } + // Some funcs (like GenerateMipmap) doesn't know the dimension, so don't check it. + if (funcDims && targetDims != funcDims) + return false; + *out = rawTexTarget; return true; } static bool -IsValidTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget, +IsValidTexImageTarget(WebGLContext* webgl, uint8_t funcDims, GLenum rawTexImageTarget, TexImageTarget* const out) { + uint8_t targetDims; + switch (rawTexImageTarget) { case LOCAL_GL_TEXTURE_2D: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: @@ -85,31 +97,38 @@ IsValidTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget, case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + targetDims = 2; break; case LOCAL_GL_TEXTURE_3D: + case LOCAL_GL_TEXTURE_2D_ARRAY: if (!webgl->IsWebGL2()) return false; + targetDims = 3; break; default: return false; } + if (targetDims != funcDims) + return false; + *out = rawTexImageTarget; return true; } bool -ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName, - TexTarget* const out_texTarget, WebGLTexture** const out_tex) +ValidateTexTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims, + GLenum rawTexTarget, TexTarget* const out_texTarget, + WebGLTexture** const out_tex) { if (webgl->IsContextLost()) return false; TexTarget texTarget; - if (!IsValidTexTarget(webgl, rawTexTarget, &texTarget)) { + if (!IsValidTexTarget(webgl, funcDims, rawTexTarget, &texTarget)) { webgl->ErrorInvalidEnum("%s: Invalid texTarget.", funcName); return false; } @@ -126,15 +145,15 @@ ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName } bool -ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget, - const char* funcName, TexImageTarget* const out_texImageTarget, +ValidateTexImageTarget(WebGLContext* webgl, const char* funcName, uint8_t funcDims, + GLenum rawTexImageTarget, TexImageTarget* const out_texImageTarget, WebGLTexture** const out_tex) { if (webgl->IsContextLost()) return false; TexImageTarget texImageTarget; - if (!IsValidTexImageTarget(webgl, rawTexImageTarget, &texImageTarget)) { + if (!IsValidTexImageTarget(webgl, funcDims, rawTexImageTarget, &texImageTarget)) { webgl->ErrorInvalidEnum("%s: Invalid texImageTarget.", funcName); return false; } @@ -150,7 +169,7 @@ ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget, return true; } -bool +/*virtual*/ bool WebGLContext::IsTexParamValid(GLenum pname) const { switch (pname) { @@ -184,32 +203,38 @@ WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex) // newTex->Target() returns a TexTarget, which will assert on invalid value. WebGLRefPtr* currentTexPtr = nullptr; switch (rawTarget) { - case LOCAL_GL_TEXTURE_2D: - currentTexPtr = &mBound2DTextures[mActiveTexture]; - break; + case LOCAL_GL_TEXTURE_2D: + currentTexPtr = &mBound2DTextures[mActiveTexture]; + break; - case LOCAL_GL_TEXTURE_CUBE_MAP: - currentTexPtr = &mBoundCubeMapTextures[mActiveTexture]; - break; - - case LOCAL_GL_TEXTURE_3D: - if (!IsWebGL2()) - return ErrorInvalidEnum("bindTexture: target TEXTURE_3D is only available in WebGL version 2.0 or newer"); + case LOCAL_GL_TEXTURE_CUBE_MAP: + currentTexPtr = &mBoundCubeMapTextures[mActiveTexture]; + break; + case LOCAL_GL_TEXTURE_3D: + if (IsWebGL2()) currentTexPtr = &mBound3DTextures[mActiveTexture]; - break; + break; - default: - return ErrorInvalidEnumInfo("bindTexture: target", rawTarget); + case LOCAL_GL_TEXTURE_2D_ARRAY: + if (IsWebGL2()) + currentTexPtr = &mBound2DArrayTextures[mActiveTexture]; + break; } + + if (!currentTexPtr) { + ErrorInvalidEnumInfo("bindTexture: target", rawTarget); + return; + } + const TexTarget texTarget(rawTarget); MakeContextCurrent(); - if (newTex && !newTex->BindTexture(texTarget)) - return; - - if (!newTex) { + if (newTex) { + if (!newTex->BindTexture(texTarget)) + return; + } else { gl->fBindTexture(texTarget.get(), 0); } @@ -219,9 +244,12 @@ WebGLContext::BindTexture(GLenum rawTarget, WebGLTexture* newTex) void WebGLContext::GenerateMipmap(GLenum rawTexTarget) { + const char funcName[] = "generateMipmap"; + const uint8_t funcDims = 0; + TexTarget texTarget; WebGLTexture* tex; - if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex)) + if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex)) return; tex->GenerateMipmap(texTarget); @@ -230,9 +258,12 @@ WebGLContext::GenerateMipmap(GLenum rawTexTarget) JS::Value WebGLContext::GetTexParameter(GLenum rawTexTarget, GLenum pname) { + const char funcName[] = "getTexParameter"; + const uint8_t funcDims = 0; + TexTarget texTarget; WebGLTexture* tex; - if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex)) + if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex)) return JS::NullValue(); if (!IsTexParamValid(pname)) { @@ -261,9 +292,12 @@ WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeI { MOZ_ASSERT(maybeIntParam || maybeFloatParam); + const char funcName[] = "texParameter"; + const uint8_t funcDims = 0; + TexTarget texTarget; WebGLTexture* tex; - if (!ValidateTexTarget(this, rawTexTarget, "texParameter", &texTarget, &tex)) + if (!ValidateTexTarget(this, funcName, funcDims, rawTexTarget, &texTarget, &tex)) return; tex->TexParameter(texTarget, pname, maybeIntParam, maybeFloatParam); @@ -274,140 +308,232 @@ WebGLContext::TexParameter_base(GLenum rawTexTarget, GLenum pname, GLint* maybeI // Uploads -////////////////////////////////////////////////////////////////////////////////////////// +//////////////////// // TexImage -void -WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, - GLenum unpackFormat, GLenum unpackType, dom::Element* elem, - ErrorResult* const out_rv) -{ - TexImageTarget texImageTarget; - WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget, - &tex)) - { - return; - } - - tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, elem, - out_rv); -} - void WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLenum unpackFormat, GLenum unpackType, const dom::Nullable& maybeView, - ErrorResult& out_rv) + ErrorResult&) { - TexImageTarget texImageTarget; + const char funcName[] = "texImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget, + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, &tex)) { return; } - tex->TexImage2D(texImageTarget, level, internalFormat, width, height, border, - unpackFormat, unpackType, maybeView, &out_rv); + const bool isSubImage = false; + const GLint xOffset = 0; + const GLint yOffset = 0; + const GLint zOffset = 0; + const GLsizei depth = 1; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, width, height, depth, border, unpackFormat, + unpackType, maybeView); } void WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, GLenum unpackFormat, GLenum unpackType, - dom::ImageData* imageData, ErrorResult& out_rv) + dom::ImageData* imageData, ErrorResult&) { - TexImageTarget texImageTarget; + const char funcName[] = "texImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "texImage2D", &texImageTarget, + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, &tex)) { return; } - tex->TexImage2D(texImageTarget, level, internalFormat, unpackFormat, unpackType, - imageData, &out_rv); + const bool isSubImage = false; + const GLint xOffset = 0; + const GLint yOffset = 0; + const GLint zOffset = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, unpackFormat, unpackType, imageData); } - -////////////////////////////////////////////////////////////////////////////////////////// -// TexSubImage - - void -WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLenum unpackFormat, GLenum unpackType, - dom::Element* elem, ErrorResult* const out_rv) +WebGLContext::TexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, + GLenum unpackFormat, GLenum unpackType, dom::Element* elem, + ErrorResult* const out_error) { - TexImageTarget texImageTarget; + const char funcName[] = "texImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget, + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, &tex)) { return; } - tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType, - elem, out_rv); + const bool isSubImage = false; + const GLint xOffset = 0; + const GLint yOffset = 0; + const GLint zOffset = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, unpackFormat, unpackType, elem, out_error); } +//////////////////////////////////////// +// TexSubImage + void WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat, GLenum unpackType, const dom::Nullable& maybeView, - ErrorResult& out_rv) + ErrorResult&) { - TexImageTarget texImageTarget; + const char funcName[] = "texSubImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget, + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, &tex)) { return; } - tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, - unpackFormat, unpackType, maybeView, &out_rv); + const bool isSubImage = true; + const GLenum internalFormat = 0; + const GLint zOffset = 0; + const GLsizei depth = 1; + const GLint border = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, width, height, depth, border, unpackFormat, + unpackType, maybeView); } void -WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, GLint yOffset, - GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, - ErrorResult& out_rv) +WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLenum unpackFormat, GLenum unpackType, + dom::ImageData* imageData, ErrorResult&) { - TexImageTarget texImageTarget; + const char funcName[] = "texSubImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "texSubImage2D", &texImageTarget, + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, &tex)) { return; } - tex->TexSubImage2D(texImageTarget, level, xOffset, yOffset, unpackFormat, unpackType, - imageData, &out_rv); + const bool isSubImage = true; + const GLenum internalFormat = 0; + const GLint zOffset = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, unpackFormat, unpackType, imageData); } +void +WebGLContext::TexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOffset, + GLint yOffset, GLenum unpackFormat, GLenum unpackType, + dom::Element* elem, ErrorResult* const out_error) +{ + const char funcName[] = "texSubImage2D"; + const uint8_t funcDims = 2; -////////////////////////////////////////////////////////////////////////////////////////// + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + const bool isSubImage = true; + const GLenum internalFormat = 0; + const GLint zOffset = 0; + tex->TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, + yOffset, zOffset, unpackFormat, unpackType, elem, out_error); +} + +//////////////////////////////////////// +// CompressedTex(Sub)Image + +void +WebGLContext::CompressedTexImage2D(GLenum rawTexImageTarget, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, + GLint border, + const dom::ArrayBufferViewOrSharedArrayBufferView& view) +{ + const char funcName[] = "compressedTexImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + const GLsizei depth = 1; + + tex->CompressedTexImage(funcName, target, level, internalFormat, width, height, depth, + border, view); +} + +void +WebGLContext::CompressedTexSubImage2D(GLenum rawTexImageTarget, GLint level, + GLint xOffset, GLint yOffset, GLsizei width, + GLsizei height, GLenum sizedUnpackFormat, + const dom::ArrayBufferViewOrSharedArrayBufferView& view) +{ + const char funcName[] = "compressedTexSubImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; + WebGLTexture* tex; + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) + { + return; + } + + const GLint zOffset = 0; + const GLsizei depth = 1; + + tex->CompressedTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, width, + height, depth, sizedUnpackFormat, view); +} + +//////////////////////////////////////// // CopyTex(Sub)Image - void WebGLContext::CopyTexImage2D(GLenum rawTexImageTarget, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) { - TexImageTarget texImageTarget; + const char funcName[] = "copyTexImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexImage2D", - &texImageTarget, &tex)) + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) { return; } - tex->CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height, - border); + tex->CopyTexImage2D(target, level, internalFormat, x, y, width, height, border); } void @@ -415,55 +541,21 @@ WebGLContext::CopyTexSubImage2D(GLenum rawTexImageTarget, GLint level, GLint xOf GLint yOffset, GLint x, GLint y, GLsizei width, GLsizei height) { - TexImageTarget texImageTarget; + const char funcName[] = "copyTexSubImage2D"; + const uint8_t funcDims = 2; + + TexImageTarget target; WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "copyTexSubImage2D", - &texImageTarget, &tex)) + if (!ValidateTexImageTarget(this, funcName, funcDims, rawTexImageTarget, &target, + &tex)) { return; } - tex->CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height); -} + const GLint zOffset = 0; - -////////////////////////////////////////////////////////////////////////////////////////// -// CompressedTex(Sub)Image - - -void -WebGLContext::CompressedTexImage2D(GLenum rawTexImageTarget, GLint level, - GLenum internalFormat, GLsizei width, GLsizei height, - GLint border, const dom::ArrayBufferViewOrSharedArrayBufferView& view) -{ - TexImageTarget texImageTarget; - WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexImage2D", - &texImageTarget, &tex)) - { - return; - } - - tex->CompressedTexImage2D(texImageTarget, level, internalFormat, width, height, - border, view); -} - -void -WebGLContext::CompressedTexSubImage2D(GLenum rawTexImageTarget, GLint level, - GLint xOffset, GLint yOffset, GLsizei width, - GLsizei height, GLenum unpackFormat, - const dom::ArrayBufferViewOrSharedArrayBufferView& view) -{ - TexImageTarget texImageTarget; - WebGLTexture* tex; - if (!ValidateTexImageTarget(this, rawTexImageTarget, "compressedTexSubImage2D", - &texImageTarget, &tex)) - { - return; - } - - tex->CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, - unpackFormat, view); + tex->CopyTexSubImage(funcName, target, level, xOffset, yOffset, zOffset, x, y, width, + height); } } // namespace mozilla diff --git a/dom/canvas/WebGLContextUtils.cpp b/dom/canvas/WebGLContextUtils.cpp index 9b7ca8e233..d7452b324a 100644 --- a/dom/canvas/WebGLContextUtils.cpp +++ b/dom/canvas/WebGLContextUtils.cpp @@ -27,38 +27,10 @@ namespace mozilla { -bool -IsGLDepthFormat(TexInternalFormat internalformat) -{ - TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat); - return unsizedformat == LOCAL_GL_DEPTH_COMPONENT; -} - -bool -IsGLDepthStencilFormat(TexInternalFormat internalformat) -{ - TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat); - return unsizedformat == LOCAL_GL_DEPTH_STENCIL; -} - -bool -FormatHasAlpha(TexInternalFormat internalformat) -{ - TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat); - return unsizedformat == LOCAL_GL_RGBA || - unsizedformat == LOCAL_GL_LUMINANCE_ALPHA || - unsizedformat == LOCAL_GL_ALPHA || - unsizedformat == LOCAL_GL_SRGB_ALPHA || - unsizedformat == LOCAL_GL_RGBA_INTEGER; -} - TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget) { switch (texImageTarget.get()) { - case LOCAL_GL_TEXTURE_2D: - case LOCAL_GL_TEXTURE_3D: - return texImageTarget.get(); case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: @@ -66,10 +38,9 @@ TexImageTargetToTexTarget(TexImageTarget texImageTarget) case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return LOCAL_GL_TEXTURE_CUBE_MAP; + default: - MOZ_ASSERT(false, "Bad texture target"); - // Should be caught by the constructor for TexTarget - return LOCAL_GL_NONE; + return texImageTarget.get(); } } @@ -85,387 +56,6 @@ StringValue(JSContext* cx, const char* chars, ErrorResult& rv) return JS::StringValue(str); } -GLComponents::GLComponents(TexInternalFormat internalformat) -{ - TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat); - mComponents = 0; - - switch (unsizedformat.get()) { - case LOCAL_GL_RGBA: - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGBA8: - case LOCAL_GL_RGB5_A1: - // Luminance + Alpha can be converted - // to and from RGBA - case LOCAL_GL_LUMINANCE_ALPHA: - mComponents |= Components::Alpha; - // Drops through - case LOCAL_GL_RGB: - case LOCAL_GL_RGB565: - // Luminance can be converted to and from RGB - case LOCAL_GL_LUMINANCE: - mComponents |= Components::Red | Components::Green | Components::Blue; - break; - case LOCAL_GL_ALPHA: - mComponents |= Components::Alpha; - break; - case LOCAL_GL_DEPTH_COMPONENT: - mComponents |= Components::Depth; - break; - case LOCAL_GL_DEPTH_STENCIL: - mComponents |= Components::Stencil; - break; - default: - MOZ_ASSERT(false, "Unhandled case - GLComponents"); - break; - } -} - -bool -GLComponents::IsSubsetOf(const GLComponents& other) const -{ - return (mComponents | other.mComponents) == other.mComponents; -} - -TexType -TypeFromInternalFormat(TexInternalFormat internalformat) -{ -#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \ - if (internalformat == table_effectiveinternalformat) { \ - return table_type; \ - } - -#include "WebGLInternalFormatsTable.h" - - // if we're here, then internalformat is not an effective internalformat i.e. is an unsized internalformat. - return LOCAL_GL_NONE; // no size, no type -} - -TexInternalFormat -UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat) -{ -#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \ - if (internalformat == table_effectiveinternalformat) { \ - return table_internalformat; \ - } - -#include "WebGLInternalFormatsTable.h" - - // if we're here, then internalformat is not an effective internalformat i.e. is an unsized internalformat. - // so we can just return it. - return internalformat; -} - -/* - * Note that the following two functions are inverse of each other: - * EffectiveInternalFormatFromInternalFormatAndType and - * InternalFormatAndTypeFromEffectiveInternalFormat both implement OpenGL ES 3.0.3 Table 3.2 - * but in opposite directions. - */ -TexInternalFormat -EffectiveInternalFormatFromUnsizedInternalFormatAndType(TexInternalFormat internalformat, - TexType type) -{ - MOZ_ASSERT(TypeFromInternalFormat(internalformat) == LOCAL_GL_NONE); - -#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \ - if (internalformat == table_internalformat && type == table_type) { \ - return table_effectiveinternalformat; \ - } - -#include "WebGLInternalFormatsTable.h" - - // If we're here, that means that type was incompatible with the given internalformat. - return LOCAL_GL_NONE; -} - -void -UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat, - TexInternalFormat* const out_internalformat, - TexType* const out_type) -{ - MOZ_ASSERT(TypeFromInternalFormat(effectiveinternalformat) != LOCAL_GL_NONE); - - MOZ_ASSERT(out_internalformat); - MOZ_ASSERT(out_type); - - GLenum internalformat = LOCAL_GL_NONE; - GLenum type = LOCAL_GL_NONE; - - switch (effectiveinternalformat.get()) { - -#define HANDLE_WEBGL_INTERNAL_FORMAT(table_effectiveinternalformat, table_internalformat, table_type) \ - case table_effectiveinternalformat: \ - internalformat = table_internalformat; \ - type = table_type; \ - break; - -#include "WebGLInternalFormatsTable.h" - - default: - MOZ_CRASH(); // impossible to get here - } - - *out_internalformat = internalformat; - *out_type = type; -} - -TexInternalFormat -EffectiveInternalFormatFromInternalFormatAndType(TexInternalFormat internalformat, - TexType type) -{ - TexType typeOfInternalFormat = TypeFromInternalFormat(internalformat); - if (typeOfInternalFormat == LOCAL_GL_NONE) - return EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, type); - - if (typeOfInternalFormat == type) - return internalformat; - - return LOCAL_GL_NONE; -} - -/** - * Convert effective internalformat into GL function parameters - * valid for underlying driver. - */ -void -DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl, - TexInternalFormat effectiveinternalformat, - GLenum* const out_driverInternalFormat, - GLenum* const out_driverFormat, - GLenum* const out_driverType) -{ - MOZ_ASSERT(out_driverInternalFormat); - MOZ_ASSERT(out_driverFormat); - MOZ_ASSERT(out_driverType); - - TexInternalFormat unsizedinternalformat = LOCAL_GL_NONE; - TexType type = LOCAL_GL_NONE; - - UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(effectiveinternalformat, - &unsizedinternalformat, - &type); - - // driverType: almost always the generic type that we just got, except on ES - // we must replace HALF_FLOAT by HALF_FLOAT_OES - GLenum driverType = type.get(); - if (gl->IsGLES() && type == LOCAL_GL_HALF_FLOAT) - driverType = LOCAL_GL_HALF_FLOAT_OES; - - // driverFormat: always just the unsized internalformat that we just got - GLenum driverFormat = unsizedinternalformat.get(); - - // driverInternalFormat: almost always the same as driverFormat, but on desktop GL, - // in some cases we must pass a different value. On ES, they are equal by definition - // as it is an error to pass internalformat!=format. - GLenum driverInternalFormat = driverFormat; - if (gl->IsCompatibilityProfile()) { - // Cases where desktop OpenGL requires a tweak to 'format' - if (driverFormat == LOCAL_GL_SRGB) - driverFormat = LOCAL_GL_RGB; - else if (driverFormat == LOCAL_GL_SRGB_ALPHA) - driverFormat = LOCAL_GL_RGBA; - - // WebGL2's new formats are not legal values for internalformat, - // as using unsized internalformat is deprecated. - if (driverFormat == LOCAL_GL_RED || - driverFormat == LOCAL_GL_RG || - driverFormat == LOCAL_GL_RED_INTEGER || - driverFormat == LOCAL_GL_RG_INTEGER || - driverFormat == LOCAL_GL_RGB_INTEGER || - driverFormat == LOCAL_GL_RGBA_INTEGER) - { - driverInternalFormat = effectiveinternalformat.get(); - } - - // Cases where desktop OpenGL requires a sized internalformat, - // as opposed to the unsized internalformat that had the same - // GLenum value as 'format', in order to get the precise - // semantics that we want. For example, for floating-point formats, - // we seem to need a sized internalformat to get non-clamped floating - // point texture sampling. Can't find the spec reference for that, - // but that's at least the case on my NVIDIA driver version 331. - if (unsizedinternalformat == LOCAL_GL_DEPTH_COMPONENT || - unsizedinternalformat == LOCAL_GL_DEPTH_STENCIL || - type == LOCAL_GL_FLOAT || - type == LOCAL_GL_HALF_FLOAT) - { - driverInternalFormat = effectiveinternalformat.get(); - } - } - - // OpenGL core profile removed texture formats ALPHA, LUMINANCE and LUMINANCE_ALPHA - if (gl->IsCoreProfile()) { - switch (driverFormat) { - case LOCAL_GL_ALPHA: - case LOCAL_GL_LUMINANCE: - driverInternalFormat = driverFormat = LOCAL_GL_RED; - break; - - case LOCAL_GL_LUMINANCE_ALPHA: - driverInternalFormat = driverFormat = LOCAL_GL_RG; - break; - } - } - - *out_driverInternalFormat = driverInternalFormat; - *out_driverFormat = driverFormat; - *out_driverType = driverType; -} - -// Map R to A -static const GLenum kLegacyAlphaSwizzle[4] = { - LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_ZERO, LOCAL_GL_RED -}; -// Map R to RGB -static const GLenum kLegacyLuminanceSwizzle[4] = { - LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_ONE -}; -// Map R to RGB, G to A -static const GLenum kLegacyLuminanceAlphaSwizzle[4] = { - LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_RED, LOCAL_GL_GREEN -}; - -void -SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat) -{ - if (!gl->IsCoreProfile()) - return; - - /* Only support swizzling on core profiles. */ - // Bug 1159117: Fix this. - // MOZ_RELEASE_ASSERT(gl->IsSupported(gl::GLFeature::texture_swizzle)); - - switch (internalformat) { - case LOCAL_GL_ALPHA: - gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA, - (GLint*) kLegacyAlphaSwizzle); - break; - - case LOCAL_GL_LUMINANCE: - gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA, - (GLint*) kLegacyLuminanceSwizzle); - break; - - case LOCAL_GL_LUMINANCE_ALPHA: - gl->fTexParameteriv(target, LOCAL_GL_TEXTURE_SWIZZLE_RGBA, - (GLint*) kLegacyLuminanceAlphaSwizzle); - break; - } -} - -/** - * Return the bits per texel for format & type combination. - * Assumes that format & type are a valid combination as checked with - * ValidateTexImageFormatAndType(). - */ -size_t -GetBitsPerTexel(TexInternalFormat effectiveinternalformat) -{ - switch (effectiveinternalformat.get()) { - case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: - return 2; - - case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case LOCAL_GL_ATC_RGB: - case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: - case LOCAL_GL_ETC1_RGB8_OES: - return 4; - - case LOCAL_GL_ALPHA8: - case LOCAL_GL_LUMINANCE8: - case LOCAL_GL_R8: - case LOCAL_GL_R8I: - case LOCAL_GL_R8UI: - case LOCAL_GL_R8_SNORM: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: - case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: - return 8; - - case LOCAL_GL_LUMINANCE8_ALPHA8: - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGB5_A1: - case LOCAL_GL_DEPTH_COMPONENT16: - case LOCAL_GL_RG8: - case LOCAL_GL_R16I: - case LOCAL_GL_R16UI: - case LOCAL_GL_RGB565: - case LOCAL_GL_R16F: - case LOCAL_GL_RG8I: - case LOCAL_GL_RG8UI: - case LOCAL_GL_RG8_SNORM: - case LOCAL_GL_ALPHA16F_EXT: - case LOCAL_GL_LUMINANCE16F_EXT: - return 16; - - case LOCAL_GL_RGB8: - case LOCAL_GL_DEPTH_COMPONENT24: - case LOCAL_GL_SRGB8: - case LOCAL_GL_RGB8UI: - case LOCAL_GL_RGB8I: - case LOCAL_GL_RGB8_SNORM: - return 24; - - case LOCAL_GL_RGBA8: - case LOCAL_GL_RGB10_A2: - case LOCAL_GL_R32F: - case LOCAL_GL_RG16F: - case LOCAL_GL_R32I: - case LOCAL_GL_R32UI: - case LOCAL_GL_RG16I: - case LOCAL_GL_RG16UI: - case LOCAL_GL_DEPTH24_STENCIL8: - case LOCAL_GL_R11F_G11F_B10F: - case LOCAL_GL_RGB9_E5: - case LOCAL_GL_SRGB8_ALPHA8: - case LOCAL_GL_DEPTH_COMPONENT32F: - case LOCAL_GL_RGBA8UI: - case LOCAL_GL_RGBA8I: - case LOCAL_GL_RGBA8_SNORM: - case LOCAL_GL_RGB10_A2UI: - case LOCAL_GL_LUMINANCE_ALPHA16F_EXT: - case LOCAL_GL_ALPHA32F_EXT: - case LOCAL_GL_LUMINANCE32F_EXT: - return 32; - - case LOCAL_GL_DEPTH32F_STENCIL8: - return 40; - - case LOCAL_GL_RGB16F: - case LOCAL_GL_RGB16UI: - case LOCAL_GL_RGB16I: - return 48; - - case LOCAL_GL_RG32F: - case LOCAL_GL_RG32I: - case LOCAL_GL_RG32UI: - case LOCAL_GL_RGBA16F: - case LOCAL_GL_RGBA16UI: - case LOCAL_GL_RGBA16I: - case LOCAL_GL_LUMINANCE_ALPHA32F_EXT: - return 64; - - case LOCAL_GL_RGB32F: - case LOCAL_GL_RGB32UI: - case LOCAL_GL_RGB32I: - return 96; - - case LOCAL_GL_RGBA32F: - case LOCAL_GL_RGBA32UI: - case LOCAL_GL_RGBA32I: - return 128; - - default: - MOZ_ASSERT(false, "Unhandled format"); - return 0; - } -} - void WebGLContext::GenerateWarning(const char* fmt, ...) { @@ -518,27 +108,6 @@ WebGLContext::ShouldGenerateWarnings() const return mAlreadyGeneratedWarnings < mMaxWarnings; } -CheckedUint32 -WebGLContext::GetImageSize(GLsizei height, GLsizei width, GLsizei depth, - uint32_t pixelSize, uint32_t packOrUnpackAlignment) -{ - CheckedUint32 checked_plainRowSize = CheckedUint32(width) * pixelSize; - - // alignedRowSize = row size rounded up to next multiple of packAlignment - CheckedUint32 checked_alignedRowSize = RoundedToNextMultipleOf(checked_plainRowSize, packOrUnpackAlignment); - - // if height is 0, we don't need any memory to store this; without this check, we'll get an overflow - CheckedUint32 checked_2dImageSize = 0; - if (height >= 1) { - checked_2dImageSize = (height-1) * checked_alignedRowSize + - checked_plainRowSize; - } - - // FIXME - we should honor UNPACK_IMAGE_HEIGHT - CheckedUint32 checked_imageSize = checked_2dImageSize * depth; - return checked_imageSize; -} - void WebGLContext::SynthesizeGLError(GLenum err) { @@ -1110,7 +679,7 @@ WebGLContext::AssertCachedBindings() GetAndFlushUnderlyingGLErrors(); - if (IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_vertex_array_object)) { GLuint bound = mBoundVertexArray ? mBoundVertexArray->GLName() : 0; AssertUintParamCorrect(gl, LOCAL_GL_VERTEX_ARRAY_BINDING, bound); } @@ -1166,10 +735,20 @@ WebGLContext::AssertCachedState() GetAndFlushUnderlyingGLErrors(); - // extensions - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) { - AssertUintParamCorrect(gl, LOCAL_GL_MAX_COLOR_ATTACHMENTS, mGLMaxColorAttachments); - AssertUintParamCorrect(gl, LOCAL_GL_MAX_DRAW_BUFFERS, mGLMaxDrawBuffers); + //////////////// + + AssertUintParamCorrect(gl, LOCAL_GL_MAX_COLOR_ATTACHMENTS, mGLMaxColorAttachments); + AssertUintParamCorrect(gl, LOCAL_GL_MAX_DRAW_BUFFERS, mGLMaxDrawBuffers); + + if (IsWebGL2() || + IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) + { + MOZ_ASSERT(mImplMaxColorAttachments == std::min(mGLMaxColorAttachments, + mGLMaxDrawBuffers)); + MOZ_ASSERT(mImplMaxDrawBuffers == mGLMaxDrawBuffers); + } else { + MOZ_ASSERT(mImplMaxColorAttachments == 1); + MOZ_ASSERT(mImplMaxDrawBuffers == 1); } // Draw state @@ -1231,8 +810,19 @@ WebGLContext::AssertCachedState() int4[2] == mViewportWidth && int4[3] == mViewportHeight); - AssertUintParamCorrect(gl, LOCAL_GL_PACK_ALIGNMENT, mPixelStorePackAlignment); - AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStoreUnpackAlignment); + AssertUintParamCorrect(gl, LOCAL_GL_PACK_ALIGNMENT, mPixelStore_PackAlignment); + AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ALIGNMENT, mPixelStore_UnpackAlignment); + + if (IsWebGL2()) { + AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_IMAGE_HEIGHT, mPixelStore_UnpackImageHeight); + AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_IMAGES , mPixelStore_UnpackSkipImages); + AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_ROW_LENGTH , mPixelStore_UnpackRowLength); + AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_ROWS , mPixelStore_UnpackSkipRows); + AssertUintParamCorrect(gl, LOCAL_GL_UNPACK_SKIP_PIXELS , mPixelStore_UnpackSkipPixels); + AssertUintParamCorrect(gl, LOCAL_GL_PACK_ROW_LENGTH , mPixelStore_PackRowLength); + AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_ROWS , mPixelStore_PackSkipRows); + AssertUintParamCorrect(gl, LOCAL_GL_PACK_SKIP_PIXELS , mPixelStore_PackSkipPixels); + } MOZ_ASSERT(!GetAndFlushUnderlyingGLErrors()); #endif diff --git a/dom/canvas/WebGLContextUtils.h b/dom/canvas/WebGLContextUtils.h index 6f6c7309d9..5401fc878f 100644 --- a/dom/canvas/WebGLContextUtils.h +++ b/dom/canvas/WebGLContextUtils.h @@ -15,35 +15,6 @@ namespace mozilla { -bool IsGLDepthFormat(TexInternalFormat webGLFormat); -bool IsGLDepthStencilFormat(TexInternalFormat webGLFormat); -bool FormatHasAlpha(TexInternalFormat webGLFormat); - -void -DriverFormatsFromEffectiveInternalFormat(gl::GLContext* gl, - TexInternalFormat internalformat, - GLenum* const out_driverInternalFormat, - GLenum* const out_driverFormat, - GLenum* const out_driverType); -TexInternalFormat -EffectiveInternalFormatFromInternalFormatAndType(TexInternalFormat internalformat, - TexType type); -TexInternalFormat -EffectiveInternalFormatFromUnsizedInternalFormatAndType(TexInternalFormat internalformat, - TexType type); -void -UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(TexInternalFormat effectiveinternalformat, - TexInternalFormat* const out_internalformat, - TexType* const out_type); -TexType TypeFromInternalFormat(TexInternalFormat internalformat); - -TexInternalFormat -UnsizedInternalFormatFromInternalFormat(TexInternalFormat internalformat); - -void SetLegacyTextureSwizzle(gl::GLContext* gl, GLenum target, GLenum internalformat); - -size_t GetBitsPerTexel(TexInternalFormat effectiveinternalformat); - // For use with the different texture calls, i.e. // TexImage2D, CopyTex[Sub]Image2D, ... // that take a "target" parameter. This parameter is not always the same as @@ -61,9 +32,6 @@ TexTarget TexImageTargetToTexTarget(TexImageTarget texImageTarget); // Helper function to create a JS::Value from a C string JS::Value StringValue(JSContext* cx, const char* str, ErrorResult& rv); -bool IsCompressedTextureFormat(GLenum format); -bool IsTextureFormatCompressed(TexInternalFormat format); - struct GLComponents { unsigned char mComponents; @@ -96,7 +64,7 @@ WebGLContext::WebGLObjectAsJSValue(JSContext* cx, const WebGLObjectType* object, if (!object) return JS::NullValue(); - MOZ_ASSERT(this == object->Context()); + MOZ_ASSERT(this == object->mContext); JS::Rooted v(cx); JS::Rooted wrapper(cx, GetWrapper()); JSAutoCompartment ac(cx, wrapper); diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp index 29dedac8f9..5a7820c5d0 100644 --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -35,119 +35,6 @@ namespace mozilla { -/** - * Return the block size for format. - */ -static void -BlockSizeFor(GLenum format, GLint* const out_blockWidth, - GLint* const out_blockHeight) -{ - MOZ_ASSERT(out_blockWidth && out_blockHeight); - - switch (format) { - case LOCAL_GL_ATC_RGB: - case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: - case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: - case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - *out_blockWidth = 4; - *out_blockHeight = 4; - break; - - case LOCAL_GL_ETC1_RGB8_OES: - // 4x4 blocks, but no 4-multiple requirement. - break; - - default: - break; - } -} - -static bool -IsCompressedFunc(WebGLTexImageFunc func) -{ - return func == WebGLTexImageFunc::CompTexImage || - func == WebGLTexImageFunc::CompTexSubImage; -} - -/** - * Same as ErrorInvalidEnum but uses WebGLContext::EnumName to print displayable - * name for \a glenum. - */ -static void -ErrorInvalidEnumWithName(WebGLContext* ctx, const char* msg, GLenum glenum, - WebGLTexImageFunc func, WebGLTexDimensions dims) -{ - const char* name = WebGLContext::EnumName(glenum); - if (name) { - ctx->ErrorInvalidEnum("%s: %s %s", InfoFrom(func, dims), msg, name); - } else { - ctx->ErrorInvalidEnum("%s: %s 0x%04x", InfoFrom(func, dims), msg, - glenum); - } -} - -/** - * Return true if the format is valid for source calls. - */ -static bool -IsAllowedFromSource(GLenum format, WebGLTexImageFunc func) -{ - switch (format) { - case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: - case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: - return (func == WebGLTexImageFunc::CompTexImage || - func == WebGLTexImageFunc::CompTexSubImage); - - case LOCAL_GL_ATC_RGB: - case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: - case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: - case LOCAL_GL_ETC1_RGB8_OES: - return func == WebGLTexImageFunc::CompTexImage; - } - - return true; -} - -/** - * Returns true if func is a CopyTexImage variant. - */ -static bool -IsCopyFunc(WebGLTexImageFunc func) -{ - return (func == WebGLTexImageFunc::CopyTexImage || - func == WebGLTexImageFunc::CopyTexSubImage); -} - -/** - * Returns true if func is a SubImage variant. - */ -static bool -IsSubFunc(WebGLTexImageFunc func) -{ - return (func == WebGLTexImageFunc::TexSubImage || - func == WebGLTexImageFunc::CopyTexSubImage || - func == WebGLTexImageFunc::CompTexSubImage); -} - -/** - * returns true is target is a texture cube map target. - */ -static bool -IsTexImageCubemapTarget(GLenum texImageTarget) -{ - return (texImageTarget >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X && - texImageTarget <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); -} - bool WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info) { @@ -159,8 +46,11 @@ WebGLContext::ValidateBlendEquationEnum(GLenum mode, const char* info) case LOCAL_GL_MIN: case LOCAL_GL_MAX: - if (IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax)) + if (IsWebGL2() || + IsExtensionEnabled(WebGLExtensionID::EXT_blend_minmax)) + { return true; + } break; @@ -373,13 +263,10 @@ WebGLContext::ValidateDrawModeEnum(GLenum mode, const char* info) } } -/** - * Return true if the framebuffer attachment is valid. Attachment must - * be one of depth/stencil/depth_stencil/color attachment. - */ bool WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum attachment, - const char* funcName) + const char* funcName, + bool badColorAttachmentIsInvalidOp) { if (!fb) { switch (attachment) { @@ -402,11 +289,21 @@ WebGLContext::ValidateFramebufferAttachment(const WebGLFramebuffer* fb, GLenum a return true; } - if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 && attachment <= LastColorAttachment()) + if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 && + attachment <= LastColorAttachmentEnum()) + { return true; + } - ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName, - attachment); + if (badColorAttachmentIsInvalidOp && + attachment >= LOCAL_GL_COLOR_ATTACHMENT0) + { + const uint32_t offset = attachment - LOCAL_GL_COLOR_ATTACHMENT0; + ErrorInvalidOperation("%s: Bad color attachment: COLOR_ATTACHMENT%u. (0x%04x)", + funcName, offset, attachment); + } else { + ErrorInvalidEnum("%s: attachment: Bad attachment 0x%x.", funcName, attachment); + } return false; } @@ -521,907 +418,6 @@ WebGLContext::ValidateSamplerParameterParams(GLenum pname, const WebGLIntOrFloat } } - -/** - * Return true if format is a valid texture image format for source, - * taking into account enabled WebGL extensions. - */ -bool -WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - /* Core WebGL texture formats */ - if (format == LOCAL_GL_ALPHA || - format == LOCAL_GL_RGB || - format == LOCAL_GL_RGBA || - format == LOCAL_GL_LUMINANCE || - format == LOCAL_GL_LUMINANCE_ALPHA) - { - return true; - } - - /* WebGL2 new formats */ - if (format == LOCAL_GL_RED || - format == LOCAL_GL_RG || - format == LOCAL_GL_RED_INTEGER || - format == LOCAL_GL_RG_INTEGER || - format == LOCAL_GL_RGB_INTEGER || - format == LOCAL_GL_RGBA_INTEGER) - { - if (IsWebGL2()) - return true; - - ErrorInvalidEnum("%s: Invalid format %s: Requires WebGL version 2.0 or" - " newer.", InfoFrom(func, dims), EnumName(format)); - return false; - } - - /* WEBGL_depth_texture added formats */ - if (format == LOCAL_GL_DEPTH_COMPONENT || - format == LOCAL_GL_DEPTH_STENCIL) - { - if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) { - ErrorInvalidEnum("%s: Invalid format %s: Requires that" - " WEBGL_depth_texture is enabled.", - InfoFrom(func, dims), EnumName(format)); - return false; - } - - // If WEBGL_depth_texture is enabled, then it is not allowed to be used - // with the copyTexImage, or copyTexSubImage methods, and it is not - // allowed with texSubImage in WebGL1. - if ((func == WebGLTexImageFunc::TexSubImage && !IsWebGL2()) || - func == WebGLTexImageFunc::CopyTexImage || - func == WebGLTexImageFunc::CopyTexSubImage) - { - ErrorInvalidOperation("%s: format %s is not supported", - InfoFrom(func, dims), EnumName(format)); - return false; - } - - return true; - } - - // Needs to be below the depth_texture check because an invalid operation - // error needs to be generated instead of invalid enum. - // Only core formats are valid for CopyTex[Sub]Image. - // TODO: Revisit this once color_buffer_[half_]float lands. - if (IsCopyFunc(func)) { - ErrorInvalidEnumWithName(this, "invalid format", format, func, dims); - return false; - } - - // EXT_sRGB added formats - if (format == LOCAL_GL_SRGB || - format == LOCAL_GL_SRGB_ALPHA) - { - if (IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) - return true; - - ErrorInvalidEnum("%s: Invalid format %s: Requires that EXT_sRGB is" - " enabled.", InfoFrom(func, dims), - WebGLContext::EnumName(format)); - return false; - } - - ErrorInvalidEnumWithName(this, "invalid format", format, func, dims); - return false; -} - -/** - * Return true if type is a valid texture image type for source, - * taking into account enabled WebGL extensions. - */ -bool -WebGLContext::ValidateTexImageType(GLenum type, WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - /* Core WebGL texture types */ - if (type == LOCAL_GL_UNSIGNED_BYTE || - type == LOCAL_GL_UNSIGNED_SHORT_5_6_5 || - type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 || - type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1) - { - return true; - } - - /* WebGL2 new types */ - if (type == LOCAL_GL_BYTE || - type == LOCAL_GL_SHORT || - type == LOCAL_GL_INT || - type == LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV || - type == LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV || - type == LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV || - type == LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV) - { - if (IsWebGL2()) - return true; - - ErrorInvalidEnum("%s: Invalid type %s: Requires WebGL version 2.0 or" - " newer.", InfoFrom(func, dims), - WebGLContext::EnumName(type)); - return false; - } - - /* OES_texture_float added types */ - if (type == LOCAL_GL_FLOAT) { - if (IsExtensionEnabled(WebGLExtensionID::OES_texture_float)) - return true; - - ErrorInvalidEnum("%s: Invalid type %s: Requires that OES_texture_float" - " is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(type)); - return false; - } - - /* OES_texture_half_float add types */ - if (type == LOCAL_GL_HALF_FLOAT) { - if (IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float)) - return true; - - ErrorInvalidEnum("%s: Invalid type %s: Requires that" - " OES_texture_half_float is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(type)); - return false; - } - - /* WEBGL_depth_texture added types */ - if (type == LOCAL_GL_UNSIGNED_SHORT || - type == LOCAL_GL_UNSIGNED_INT || - type == LOCAL_GL_UNSIGNED_INT_24_8) - { - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) - return true; - - ErrorInvalidEnum("%s: Invalid type %s: Requires that" - " WEBGL_depth_texture is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(type)); - return false; - } - - ErrorInvalidEnumWithName(this, "invalid type", type, func, dims); - return false; -} - -/** - * Validate texture image sizing extra constraints for - * CompressedTex(Sub)?Image. - */ -// TODO: WebGL 2 -bool -WebGLContext::ValidateCompTexImageSize(GLint level, GLenum format, - GLint xoffset, GLint yoffset, - GLsizei width, GLsizei height, - GLsizei levelWidth, GLsizei levelHeight, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - // Negative parameters must already have been handled above - MOZ_ASSERT(xoffset >= 0 && yoffset >= 0 && - width >= 0 && height >= 0); - - if (xoffset + width > (GLint) levelWidth) { - ErrorInvalidValue("%s: xoffset + width must be <= levelWidth.", - InfoFrom(func, dims)); - return false; - } - - if (yoffset + height > (GLint) levelHeight) { - ErrorInvalidValue("%s: yoffset + height must be <= levelHeight.", - InfoFrom(func, dims)); - return false; - } - - GLint blockWidth = 1; - GLint blockHeight = 1; - BlockSizeFor(format, &blockWidth, &blockHeight); - - // If blockWidth || blockHeight != 1, then the compressed format had - // block-based constraints to be checked. (For example, PVRTC is compressed - // but isn't a block-based format) - if (blockWidth != 1 || blockHeight != 1) { - // Offsets must be multiple of block size. - if (xoffset % blockWidth != 0) { - ErrorInvalidOperation("%s: xoffset must be multiple of %d.", - InfoFrom(func, dims), blockWidth); - return false; - } - - if (yoffset % blockHeight != 0) { - ErrorInvalidOperation("%s: yoffset must be multiple of %d.", - InfoFrom(func, dims), blockHeight); - return false; - } - - /* The size must be a multiple of blockWidth and blockHeight, or must be - * using offset+size that exactly hits the edge. Important for small - * mipmap levels. - * - * From the WEBGL_compressed_texture_s3tc spec: - * When level equals zero width and height must be a multiple of 4. - * When level is greater than 0 width and height must be 0, 1, 2 or - * a multiple of 4. If they are not an INVALID_OPERATION error is - * generated." - */ - if (level == 0) { - if (width % blockWidth != 0) { - ErrorInvalidOperation("%s: Width of level 0 must be a multiple" - " of %d.", InfoFrom(func, dims), - blockWidth); - return false; - } - - if (height % blockHeight != 0) { - ErrorInvalidOperation("%s: Height of level 0 must be a multiple" - " of %d.", InfoFrom(func, dims), - blockHeight); - return false; - } - } else if (level > 0) { - if (width % blockWidth != 0 && width > 2) { - ErrorInvalidOperation("%s: Width of level %d must be a multiple" - " of %d, or be 0, 1, or 2.", - InfoFrom(func, dims), level, blockWidth); - return false; - } - - if (height % blockHeight != 0 && height > 2) { - ErrorInvalidOperation("%s: Height of level %d must be a" - " multiple of %d, or be 0, 1, or 2.", - InfoFrom(func, dims), level, blockHeight); - return false; - } - } - - if (IsSubFunc(func)) { - if ((xoffset % blockWidth) != 0) { - ErrorInvalidOperation("%s: xoffset must be a multiple of %d.", - InfoFrom(func, dims), blockWidth); - return false; - } - - if (yoffset % blockHeight != 0) { - ErrorInvalidOperation("%s: yoffset must be a multiple of %d.", - InfoFrom(func, dims), blockHeight); - return false; - } - } - } - - switch (format) { - case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: - case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: - if (!IsPOTAssumingNonnegative(width) || - !IsPOTAssumingNonnegative(height)) - { - ErrorInvalidValue("%s: Width and height must be powers of two.", - InfoFrom(func, dims)); - return false; - } - } - - return true; -} - -/** - * Return true if the enough data is present to satisfy compressed - * texture format constraints. - */ -bool -WebGLContext::ValidateCompTexImageDataSize(GLint level, GLenum format, - GLsizei width, GLsizei height, - uint32_t byteLength, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - // negative width and height must already have been handled above - MOZ_ASSERT(width >= 0 && height >= 0); - - CheckedUint32 required_byteLength = 0; - - switch (format) { - case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - case LOCAL_GL_ATC_RGB: - case LOCAL_GL_ETC1_RGB8_OES: - required_byteLength = ((CheckedUint32(width) + 3) / 4) * - ((CheckedUint32(height) + 3) / 4) * 8; - break; - - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA: - case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA: - required_byteLength = ((CheckedUint32(width) + 3) / 4) * - ((CheckedUint32(height) + 3) / 4) * 16; - break; - - case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1: - required_byteLength = CheckedUint32(std::max(width, 8)) * - CheckedUint32(std::max(height, 8)) / 2; - break; - - case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1: - case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1: - required_byteLength = CheckedUint32(std::max(width, 16)) * - CheckedUint32(std::max(height, 8)) / 4; - break; - } - - if (!required_byteLength.isValid() || - required_byteLength.value() != byteLength) - { - ErrorInvalidValue("%s: Data size does not match dimensions.", - InfoFrom(func, dims)); - return false; - } - - return true; -} - -/** - * Validate the width, height, and depth of a texture image, \return - * true is valid, false otherwise. - * Used by all the (Compressed|Copy)?Tex(Sub)?Image functions. - * Target and level must have been validated before calling. - */ -bool -WebGLContext::ValidateTexImageSize(TexImageTarget texImageTarget, GLint level, - GLint width, GLint height, GLint depth, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - MOZ_ASSERT(level >= 0, "level should already be validated"); - - /* Bug 966630: maxTextureSize >> level runs into "undefined" - * behaviour depending on ISA. For example, on Intel shifts - * amounts are mod 64 (in 64-bit mode on 64-bit dest) and mod 32 - * otherwise. This means 16384 >> 0x10000001 == 8192 which isn't - * what would be expected. Make the required behaviour explicit by - * clamping to a shift of 31 bits if level is greater than that - * ammount. This will give 0 that if (!maxAllowedSize) is - * expecting. - */ - - if (level > 31) - level = 31; - - auto texTarget = TexImageTargetToTexTarget(texImageTarget); - const GLuint maxTexImageSize = MaxTextureSizeForTarget(texTarget) >> level; - - const bool isCubemapTarget = IsTexImageCubemapTarget(texImageTarget.get()); - const bool isSub = IsSubFunc(func); - - if (!isSub && isCubemapTarget && (width != height)) { - /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification - * "When the target parameter to TexImage2D is one of the - * six cube map two-dimensional image targets, the error - * INVALID_VALUE is generated if the width and height - * parameters are not equal." - */ - ErrorInvalidValue("%s: For cube maps, width must equal height.", - InfoFrom(func, dims)); - return false; - } - - if (texImageTarget == LOCAL_GL_TEXTURE_2D || isCubemapTarget) { - /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification - * "If wt and ht are the specified image width and height, - * and if either wt or ht are less than zero, then the error - * INVALID_VALUE is generated." - */ - if (width < 0) { - ErrorInvalidValue("%s: Width must be >= 0.", InfoFrom(func, dims)); - return false; - } - - if (height < 0) { - ErrorInvalidValue("%s: Height must be >= 0.", InfoFrom(func, dims)); - return false; - } - - /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification - * "The maximum allowable width and height of a - * two-dimensional texture image must be at least 2**(k−lod) - * for image arrays of level zero through k, where k is the - * log base 2 of MAX_TEXTURE_SIZE. and lod is the - * level-of-detail of the image array. It may be zero for - * image arrays of any level-of-detail greater than k. The - * error INVALID_VALUE is generated if the specified image - * is too large to be stored under any conditions. - */ - if (width > (int) maxTexImageSize) { - ErrorInvalidValue("%s: The maximum width for level %d is %u.", - InfoFrom(func, dims), level, maxTexImageSize); - return false; - } - - if (height > (int) maxTexImageSize) { - ErrorInvalidValue("%s: The maximum height for level %d is %u.", - InfoFrom(func, dims), level, maxTexImageSize); - return false; - } - - /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification - * "If level is greater than zero, and either width or - * height is not a power-of-two, the error INVALID_VALUE is - * generated." - * - * This restriction does not apply to GL ES Version 3.0+. - */ - if (!IsWebGL2() && level > 0) { - if (!IsPOTAssumingNonnegative(width)) { - ErrorInvalidValue("%s: For level > 0, width of %d must be a" - " power of two.", InfoFrom(func, dims), - width); - return false; - } - - if (!IsPOTAssumingNonnegative(height)) { - ErrorInvalidValue("%s: For level > 0, height of %d must be a" - " power of two.", InfoFrom(func, dims), - height); - return false; - } - } - } - - // TODO: WebGL 2 - if (texImageTarget == LOCAL_GL_TEXTURE_3D) { - if (depth < 0) { - ErrorInvalidValue("%s: Depth must be >= 0.", InfoFrom(func, dims)); - return false; - } - - if (!IsWebGL2() && !IsPOTAssumingNonnegative(depth)) { - ErrorInvalidValue("%s: Depth of %d must be a power of two.", - InfoFrom(func, dims), depth); - return false; - } - } - - return true; -} - -/** - * Validate texture image sizing for Tex(Sub)?Image variants. - */ -// TODO: WebGL 2. Update this to handle 3D textures. -bool -WebGLContext::ValidateTexSubImageSize(GLint xoffset, GLint yoffset, - GLint /*zoffset*/, GLsizei width, - GLsizei height, GLsizei /*depth*/, - GLsizei baseWidth, GLsizei baseHeight, - GLsizei /*baseDepth*/, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification - * "Taking wt and ht to be the specified width and height of the - * texture array, and taking x, y, w, and h to be the xoffset, - * yoffset, width, and height argument values, any of the - * following relationships generates the error INVALID_VALUE: - * x < 0 - * x + w > wt - * y < 0 - * y + h > ht" - */ - - if (xoffset < 0) { - ErrorInvalidValue("%s: xoffset must be >= 0.", InfoFrom(func, dims)); - return false; - } - - if (yoffset < 0) { - ErrorInvalidValue("%s: yoffset must be >= 0.", InfoFrom(func, dims)); - return false; - } - - if (!CanvasUtils::CheckSaneSubrectSize(xoffset, yoffset, width, height, - baseWidth, baseHeight)) - { - ErrorInvalidValue("%s: Subtexture rectangle out-of-bounds.", - InfoFrom(func, dims)); - return false; - } - - return true; -} - -/** - * Perform validation of format/type combinations for TexImage variants. - * Returns true if the format/type is a valid combination, false otherwise. - */ -bool -WebGLContext::ValidateTexImageFormatAndType(GLenum format, GLenum type, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - if (type == LOCAL_GL_HALF_FLOAT_OES) { - type = LOCAL_GL_HALF_FLOAT; - } - - if (IsCompressedFunc(func) || IsCopyFunc(func)) { - MOZ_ASSERT(type == LOCAL_GL_NONE && format == LOCAL_GL_NONE); - return true; - } - - if (!ValidateTexImageFormat(format, func, dims) || - !ValidateTexImageType(type, func, dims)) - { - return false; - } - - // Here we're reinterpreting format as an unsized internalformat; - // these are the same in practice and there's no point in having the - // same code implemented twice. - TexInternalFormat effective = - EffectiveInternalFormatFromInternalFormatAndType(format, type); - - if (effective != LOCAL_GL_NONE) - return true; - - ErrorInvalidOperation("%s: Invalid combination of format %s and type %s.", - InfoFrom(func, dims), WebGLContext::EnumName(format), - WebGLContext::EnumName(type)); - return false; -} - -bool -WebGLContext::ValidateCompTexImageInternalFormat(GLenum format, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - if (!IsCompressedTextureFormat(format)) { - ErrorInvalidEnum("%s: Invalid compressed texture format: %s", - InfoFrom(func, dims), WebGLContext::EnumName(format)); - return false; - } - - /* WEBGL_compressed_texture_atc added formats */ - if (format == LOCAL_GL_ATC_RGB || - format == LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA || - format == LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA) - { - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_atc)) - return true; - - ErrorInvalidEnum("%s: Invalid format %s: Requires that" - " WEBGL_compressed_texture_atc is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(format)); - return false; - } - - // WEBGL_compressed_texture_etc1 - if (format == LOCAL_GL_ETC1_RGB8_OES) { - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_etc1)) - return true; - - ErrorInvalidEnum("%s: Invalid format %s: Requires that" - " WEBGL_compressed_texture_etc1 is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(format)); - return false; - } - - - if (format == LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1 || - format == LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1 || - format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1 || - format == LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1) - { - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_pvrtc)) - return true; - - ErrorInvalidEnum("%s: Invalid format %s: Requires that" - " WEBGL_compressed_texture_pvrtc is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(format)); - return false; - } - - - if (format == LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT || - format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || - format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || - format == LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) - { - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_compressed_texture_s3tc)) - return true; - - ErrorInvalidEnum("%s: Invalid format %s: Requires that" - " WEBGL_compressed_texture_s3tc is enabled.", - InfoFrom(func, dims), WebGLContext::EnumName(format)); - return false; - } - - MOZ_ASSERT(false); - return false; -} - -bool -WebGLContext::ValidateCopyTexImageInternalFormat(GLenum format, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - switch (format) { - case LOCAL_GL_RGBA: - case LOCAL_GL_RGB: - case LOCAL_GL_LUMINANCE_ALPHA: - case LOCAL_GL_LUMINANCE: - case LOCAL_GL_ALPHA: - return true; - } - // In CopyTexImage, internalFormat is a function parameter, - // so a bad value is an INVALID_ENUM error. - // In CopyTexSubImage, internalFormat is part of existing state, - // so this is an INVALID_OPERATION error. - GenerateWarning("%s: Invalid texture internal format: %s", - InfoFrom(func, dims), WebGLContext::EnumName(format)); - - GLenum error; - if (func == WebGLTexImageFunc::CopyTexImage) - error = LOCAL_GL_INVALID_ENUM; - else - error = LOCAL_GL_INVALID_OPERATION; - - SynthesizeGLError(error); - return false; -} -/** - * Return true if format, type and jsArrayType are a valid combination. - * Also returns the size for texel of format and type (in bytes) via - * \a texelSize. - * - * It is assumed that type has previously been validated. - */ -bool -WebGLContext::ValidateTexInputData(GLenum type, js::Scalar::Type jsArrayType, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - // We're using js::Scalar::MaxTypedArrayViewType as dummy value for when - // the tex source wasn't a typed array. - if (jsArrayType == js::Scalar::MaxTypedArrayViewType) - return true; - - const char invalidTypedArray[] = "%s: Invalid typed array type for given" - " texture data type."; - - bool validInput = false; - switch (type) { - case LOCAL_GL_UNSIGNED_BYTE: - validInput = jsArrayType == js::Scalar::Uint8; - break; - - case LOCAL_GL_BYTE: - validInput = jsArrayType == js::Scalar::Int8; - break; - - case LOCAL_GL_HALF_FLOAT: - case LOCAL_GL_UNSIGNED_SHORT: - case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: - case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: - case LOCAL_GL_UNSIGNED_SHORT_5_6_5: - validInput = jsArrayType == js::Scalar::Uint16; - break; - - case LOCAL_GL_SHORT: - validInput = jsArrayType == js::Scalar::Int16; - break; - - case LOCAL_GL_UNSIGNED_INT: - case LOCAL_GL_UNSIGNED_INT_24_8: - case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: - case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: - case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: - validInput = jsArrayType == js::Scalar::Uint32; - break; - - case LOCAL_GL_INT: - validInput = jsArrayType == js::Scalar::Int32; - break; - - case LOCAL_GL_FLOAT: - validInput = jsArrayType == js::Scalar::Float32; - break; - - default: - break; - } - - if (!validInput) - ErrorInvalidOperation(invalidTypedArray, InfoFrom(func, dims)); - - return validInput; -} - -/** - * Checks specific for the CopyTex[Sub]Image2D functions. - * Verifies: - * - Framebuffer is complete and has valid read planes - * - Copy format is a subset of framebuffer format (i.e. all required components - * are available) - */ -bool -WebGLContext::ValidateCopyTexImage(GLenum format, WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - MOZ_ASSERT(IsCopyFunc(func)); - - // Default framebuffer format - GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB; - - if (mBoundReadFramebuffer) { - TexInternalFormat srcFormat; - if (!mBoundReadFramebuffer->ValidateForRead(InfoFrom(func, dims), &srcFormat)) - return false; - - fboFormat = srcFormat.get(); - } - - // Make sure the format of the framebuffer is a superset of the format - // requested by the CopyTex[Sub]Image2D functions. - const GLComponents formatComps = GLComponents(format); - const GLComponents fboComps = GLComponents(fboFormat); - if (!formatComps.IsSubsetOf(fboComps)) { - ErrorInvalidOperation("%s: Format %s is not a subset of the current" - " framebuffer format, which is %s.", - InfoFrom(func, dims), EnumName(format), - EnumName(fboFormat)); - return false; - } - - return true; -} - -/** - * Test the gl(Copy|Compressed)?Tex[Sub]?Image[23]() parameters for errors. - * Verifies each of the parameters against the WebGL standard and enabled - * extensions. - */ -// TODO: Texture dims is here for future expansion in WebGL 2.0 -bool -WebGLContext::ValidateTexImage(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLint xoffset, - GLint yoffset, GLint zoffset, GLint width, - GLint height, GLint depth, GLint border, - GLenum format, GLenum type, - WebGLTexImageFunc func, - WebGLTexDimensions dims) -{ - const char* info = InfoFrom(func, dims); - - // Check level - if (level < 0) { - ErrorInvalidValue("%s: `level` must be >= 0.", info); - return false; - } - - // Check border - if (border != 0) { - ErrorInvalidValue("%s: `border` must be 0.", info); - return false; - } - - // Check incoming image format and type - if (!ValidateTexImageFormatAndType(format, type, func, dims)) - return false; - - if (!TexInternalFormat::IsValueLegal(internalFormat)) { - ErrorInvalidEnum("%s: Invalid `internalformat` enum %s.", info, - EnumName(internalFormat)); - return false; - } - TexInternalFormat unsizedInternalFormat = - UnsizedInternalFormatFromInternalFormat(internalFormat); - - if (IsCompressedFunc(func)) { - if (!ValidateCompTexImageInternalFormat(internalFormat, func, dims)) - return false; - - } else if (IsCopyFunc(func)) { - if (!ValidateCopyTexImageInternalFormat(unsizedInternalFormat.get(), - func, dims)) - { - return false; - } - } else if (format != unsizedInternalFormat) { - if (IsWebGL2()) { - // In WebGL2, it's OK to have `internalFormat != format` if - // internalFormat is the sized internal format corresponding to the - // (format, type) pair according to Table 3.2 in the OpenGL ES 3.0.3 - // spec. - auto effectiveFormat = EffectiveInternalFormatFromInternalFormatAndType(format, - type); - if (internalFormat != effectiveFormat) { - bool exceptionallyAllowed = false; - if (internalFormat == LOCAL_GL_SRGB8_ALPHA8 && - format == LOCAL_GL_RGBA && - type == LOCAL_GL_UNSIGNED_BYTE) - { - exceptionallyAllowed = true; - } - else if (internalFormat == LOCAL_GL_SRGB8 && - format == LOCAL_GL_RGB && - type == LOCAL_GL_UNSIGNED_BYTE) - { - exceptionallyAllowed = true; - } - if (!exceptionallyAllowed) { - ErrorInvalidOperation("%s: `internalformat` does not match" - " `format` and `type`.", info); - return false; - } - } - } else { - // In WebGL 1, format must be equal to internalformat. - ErrorInvalidOperation("%s: `internalformat` does not match" - " `format`.", info); - return false; - } - } - - // Check texture image size - if (!ValidateTexImageSize(texImageTarget, level, width, height, 0, func, - dims)) - { - return false; - } - - /* 5.14.8 Texture objects - WebGL Spec. - * "If an attempt is made to call these functions with no - * WebGLTexture bound (see above), an INVALID_OPERATION error - * is generated." - */ - WebGLTexture* tex = ActiveBoundTextureForTexImageTarget(texImageTarget); - if (!tex) { - ErrorInvalidOperation("%s: No texture is bound to target %s.", info, - WebGLContext::EnumName(texImageTarget.get())); - return false; - } - - if (IsSubFunc(func)) { - if (!tex->HasImageInfoAt(texImageTarget, level)) { - ErrorInvalidOperation("%s: No texture image previously defined for" - " target %s at level %d.", info, - WebGLContext::EnumName(texImageTarget.get()), - level); - return false; - } - - const auto& imageInfo = tex->ImageInfoAt(texImageTarget, level); - if (!ValidateTexSubImageSize(xoffset, yoffset, zoffset, width, height, - depth, imageInfo.Width(), - imageInfo.Height(), 0, func, dims)) - { - return false; - } - } - - // Additional checks for depth textures - if (texImageTarget != LOCAL_GL_TEXTURE_2D && - (format == LOCAL_GL_DEPTH_COMPONENT || - format == LOCAL_GL_DEPTH_STENCIL)) - { - ErrorInvalidOperation("%s: With format of %s, target must be" - " TEXTURE_2D.", info, - WebGLContext::EnumName(format)); - return false; - } - - // Additional checks for compressed textures - if (!IsAllowedFromSource(internalFormat, func)) { - ErrorInvalidOperation("%s: Invalid format %s for this operation.", - info, WebGLContext::EnumName(format)); - return false; - } - - // Parameters are OK - return true; -} - bool WebGLContext::ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName) { @@ -1577,7 +573,7 @@ WebGLContext::ValidateAttribPointer(bool integerMode, GLuint index, GLint size, return false; // requiredAlignment should always be a power of two - MOZ_ASSERT(IsPOTAssumingNonnegative(requiredAlignment)); + MOZ_ASSERT(IsPowerOfTwo(requiredAlignment)); GLsizei requiredAlignmentMask = requiredAlignment - 1; if (size < 1 || size > 4) { @@ -1657,7 +653,9 @@ WebGLContext::InitAndValidateGL() // Unconditionally create a new format usage authority. This is // important when restoring contexts and extensions need to add // formats back into the authority. - mFormatUsage = CreateFormatUsage(); + mFormatUsage = CreateFormatUsage(gl); + if (!mFormatUsage) + return false; GLenum error = gl->fGetError(); if (error != LOCAL_GL_NO_ERROR) { @@ -1719,6 +717,8 @@ WebGLContext::InitAndValidateGL() // Bindings, etc. mActiveTexture = 0; + mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK; + mEmitContextLostErrorOnce = true; mWebGLError = LOCAL_GL_NO_ERROR; mUnderlyingGLError = LOCAL_GL_NO_ERROR; @@ -1726,6 +726,7 @@ WebGLContext::InitAndValidateGL() mBound2DTextures.Clear(); mBoundCubeMapTextures.Clear(); mBound3DTextures.Clear(); + mBound2DArrayTextures.Clear(); mBoundSamplers.Clear(); mBoundArrayBuffer = nullptr; @@ -1770,19 +771,33 @@ WebGLContext::InitAndValidateGL() mBound2DTextures.SetLength(mGLMaxTextureUnits); mBoundCubeMapTextures.SetLength(mGLMaxTextureUnits); mBound3DTextures.SetLength(mGLMaxTextureUnits); + mBound2DArrayTextures.SetLength(mGLMaxTextureUnits); mBoundSamplers.SetLength(mGLMaxTextureUnits); + //////////////// + if (MinCapabilityMode()) { - mGLMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; - mGLMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; - mGLMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; + mImplMaxTextureSize = MINVALUE_GL_MAX_TEXTURE_SIZE; + mImplMaxCubeMapTextureSize = MINVALUE_GL_MAX_CUBE_MAP_TEXTURE_SIZE; + mImplMaxRenderbufferSize = MINVALUE_GL_MAX_RENDERBUFFER_SIZE; + + mImplMax3DTextureSize = MINVALUE_GL_MAX_3D_TEXTURE_SIZE; + mImplMaxArrayTextureLayers = MINVALUE_GL_MAX_ARRAY_TEXTURE_LAYERS; + mGLMaxTextureImageUnits = MINVALUE_GL_MAX_TEXTURE_IMAGE_UNITS; mGLMaxVertexTextureImageUnits = MINVALUE_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS; + mGLMaxSamples = 1; } else { - gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mGLMaxTextureSize); - gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mGLMaxCubeMapTextureSize); - gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mGLMaxRenderbufferSize); + gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, (GLint*)&mImplMaxTextureSize); + gl->fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&mImplMaxCubeMapTextureSize); + gl->fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, (GLint*)&mImplMaxRenderbufferSize); + + if (!gl->GetPotentialInteger(LOCAL_GL_MAX_3D_TEXTURE_SIZE, (GLint*)&mImplMax3DTextureSize)) + mImplMax3DTextureSize = 0; + if (!gl->GetPotentialInteger(LOCAL_GL_MAX_ARRAY_TEXTURE_LAYERS, (GLint*)&mImplMaxArrayTextureLayers)) + mImplMaxArrayTextureLayers = 0; + gl->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS, &mGLMaxTextureImageUnits); gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGLMaxVertexTextureImageUnits); @@ -1790,21 +805,44 @@ WebGLContext::InitAndValidateGL() mGLMaxSamples = 1; } - // Calculate log2 of mGLMaxTextureSize and mGLMaxCubeMapTextureSize - mGLMaxTextureSizeLog2 = 0; - int32_t tempSize = mGLMaxTextureSize; - while (tempSize >>= 1) { - ++mGLMaxTextureSizeLog2; + // If we don't support a target, its max size is 0. We should only floor-to-POT if the + // value if it's non-zero. (NB log2(0) is -Inf, so zero isn't an integer power-of-two) + const auto fnFloorPOTIfSupported = [](uint32_t& val) { + if (val) { + val = FloorPOT(val); + } + }; + + fnFloorPOTIfSupported(mImplMaxTextureSize); + fnFloorPOTIfSupported(mImplMaxCubeMapTextureSize); + fnFloorPOTIfSupported(mImplMaxRenderbufferSize); + + fnFloorPOTIfSupported(mImplMax3DTextureSize); + fnFloorPOTIfSupported(mImplMaxArrayTextureLayers); + + //////////////// + + mGLMaxColorAttachments = 1; + mGLMaxDrawBuffers = 1; + gl->GetPotentialInteger(LOCAL_GL_MAX_COLOR_ATTACHMENTS, + (GLint*)&mGLMaxColorAttachments); + gl->GetPotentialInteger(LOCAL_GL_MAX_DRAW_BUFFERS, (GLint*)&mGLMaxDrawBuffers); + + if (MinCapabilityMode()) { + mGLMaxColorAttachments = std::min(mGLMaxColorAttachments, + kMinMaxColorAttachments); + mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, kMinMaxDrawBuffers); } - mGLMaxCubeMapTextureSizeLog2 = 0; - tempSize = mGLMaxCubeMapTextureSize; - while (tempSize >>= 1) { - ++mGLMaxCubeMapTextureSizeLog2; + if (IsWebGL2()) { + mImplMaxColorAttachments = mGLMaxColorAttachments; + mImplMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mImplMaxColorAttachments); + } else { + mImplMaxColorAttachments = 1; + mImplMaxDrawBuffers = 1; } - mGLMaxTextureSize = FloorPOT(mGLMaxTextureSize); - mGLMaxRenderbufferSize = FloorPOT(mGLMaxRenderbufferSize); + //////////////// if (MinCapabilityMode()) { mGLMaxFragmentUniformVectors = MINVALUE_GL_MAX_FRAGMENT_UNIFORM_VECTORS; @@ -1935,6 +973,22 @@ WebGLContext::InitAndValidateGL() mDefaultVertexArray->BindVertexArray(); } + mPixelStore_FlipY = false; + mPixelStore_PremultiplyAlpha = false; + mPixelStore_ColorspaceConversion = BROWSER_DEFAULT_WEBGL; + + // GLES 3.0.4, p259: + mPixelStore_UnpackImageHeight = 0; + mPixelStore_UnpackSkipImages = 0; + mPixelStore_UnpackRowLength = 0; + mPixelStore_UnpackSkipRows = 0; + mPixelStore_UnpackSkipPixels = 0; + mPixelStore_UnpackAlignment = 4; + mPixelStore_PackRowLength = 0; + mPixelStore_PackSkipRows = 0; + mPixelStore_PackSkipPixels = 0; + mPixelStore_PackAlignment = 4; + return true; } diff --git a/dom/canvas/WebGLContextVertices.cpp b/dom/canvas/WebGLContextVertices.cpp index f36f806349..226c9c01ab 100644 --- a/dom/canvas/WebGLContextVertices.cpp +++ b/dom/canvas/WebGLContextVertices.cpp @@ -358,8 +358,11 @@ WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname, return JS::NumberValue(uint32_t(mBoundVertexArray->mAttribs[index].type)); case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR: - if (IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) + if (IsWebGL2() || + IsExtensionEnabled(WebGLExtensionID::ANGLE_instanced_arrays)) + { return JS::Int32Value(mBoundVertexArray->mAttribs[index].divisor); + } break; case LOCAL_GL_CURRENT_VERTEX_ATTRIB: diff --git a/dom/canvas/WebGLExtensionColorBufferFloat.cpp b/dom/canvas/WebGLExtensionColorBufferFloat.cpp index 89c9cb1d8a..2c81eab102 100644 --- a/dom/canvas/WebGLExtensionColorBufferFloat.cpp +++ b/dom/canvas/WebGLExtensionColorBufferFloat.cpp @@ -9,32 +9,31 @@ #include "WebGLContext.h" #include "WebGLFormats.h" -namespace mozilla { +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif -using mozilla::webgl::EffectiveFormat; +namespace mozilla { WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* webgl) : WebGLExtensionBase(webgl) { MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); + auto& fua = webgl->mFormatUsage; - auto updateUsage = [authority](EffectiveFormat effectiveFormat) { - webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat); - MOZ_ASSERT(usage); - usage->asRenderbuffer = usage->isRenderable = true; + auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) { + auto usage = fua->EditUsage(effFormat); + usage->isRenderable = true; + fua->AllowRBFormat(sizedFormat, usage); }; - // Ensure require formats are initialized. - WebGLExtensionTextureFloat::InitWebGLFormats(authority); +#define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x) - // Update usage to allow asRenderbuffer and isRenderable - updateUsage(EffectiveFormat::RGBA32F); - updateUsage(EffectiveFormat::RGB32F); - updateUsage(EffectiveFormat::Luminance32FAlpha32F); - updateUsage(EffectiveFormat::Luminance32F); - updateUsage(EffectiveFormat::Alpha32F); + // The extension doesn't actually add RGB32F; only RGBA32F. + FOO(RGBA32F); + +#undef FOO } WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat() diff --git a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp index e80535c481..5b89fcc9e6 100644 --- a/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp +++ b/dom/canvas/WebGLExtensionColorBufferHalfFloat.cpp @@ -9,32 +9,31 @@ #include "WebGLContext.h" #include "WebGLFormats.h" -namespace mozilla { +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif -using mozilla::webgl::EffectiveFormat; +namespace mozilla { WebGLExtensionColorBufferHalfFloat::WebGLExtensionColorBufferHalfFloat(WebGLContext* webgl) : WebGLExtensionBase(webgl) { MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); + auto& fua = webgl->mFormatUsage; - auto updateUsage = [authority](EffectiveFormat effectiveFormat) { - webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat); - MOZ_ASSERT(usage); - usage->asRenderbuffer = usage->isRenderable = true; + auto fnUpdateUsage = [&fua](GLenum sizedFormat, webgl::EffectiveFormat effFormat) { + auto usage = fua->EditUsage(effFormat); + usage->isRenderable = true; + fua->AllowRBFormat(sizedFormat, usage); }; - // Ensure require formats are initialized. - WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority); +#define FOO(x) fnUpdateUsage(LOCAL_GL_ ## x, webgl::EffectiveFormat::x) - // Update usage to allow asRenderbuffer and isRenderable - updateUsage(EffectiveFormat::RGBA16F); - updateUsage(EffectiveFormat::RGB16F); - updateUsage(EffectiveFormat::Luminance16FAlpha16F); - updateUsage(EffectiveFormat::Luminance16F); - updateUsage(EffectiveFormat::Alpha16F); + FOO(RGBA16F); + FOO(RGB16F); + +#undef FOO } WebGLExtensionColorBufferHalfFloat::~WebGLExtensionColorBufferHalfFloat() diff --git a/dom/canvas/WebGLExtensionCompressedTextureATC.cpp b/dom/canvas/WebGLExtensionCompressedTextureATC.cpp index dfc4884e83..5834b9fd17 100644 --- a/dom/canvas/WebGLExtensionCompressedTextureATC.cpp +++ b/dom/canvas/WebGLExtensionCompressedTextureATC.cpp @@ -7,14 +7,33 @@ #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif + namespace mozilla { WebGLExtensionCompressedTextureATC::WebGLExtensionCompressedTextureATC(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ATC_RGB); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA); + RefPtr webgl_ = webgl; // Bug 1201275 + const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) { + auto& fua = webgl_->mFormatUsage; + + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; + fua->AllowSizedTexFormat(sizedFormat, usage); + + webgl_->mCompressedTextureFormats.AppendElement(sizedFormat); + }; + +#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x + + fnAdd(FOO(ATC_RGB_AMD)); + fnAdd(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD)); + fnAdd(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD)); + +#undef FOO } WebGLExtensionCompressedTextureATC::~WebGLExtensionCompressedTextureATC() diff --git a/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp b/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp index 7367e0f22a..1b96feac90 100644 --- a/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp +++ b/dom/canvas/WebGLExtensionCompressedTextureETC1.cpp @@ -7,12 +7,31 @@ #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif + namespace mozilla { WebGLExtensionCompressedTextureETC1::WebGLExtensionCompressedTextureETC1(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_ETC1_RGB8_OES); + RefPtr webgl_ = webgl; // Bug 1201275 + const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) { + auto& fua = webgl_->mFormatUsage; + + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; + fua->AllowSizedTexFormat(sizedFormat, usage); + + webgl_->mCompressedTextureFormats.AppendElement(sizedFormat); + }; + +#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x + + fnAdd(FOO(ETC1_RGB8_OES)); + +#undef FOO } WebGLExtensionCompressedTextureETC1::~WebGLExtensionCompressedTextureETC1() diff --git a/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp b/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp index 7af2e1fc35..366d0080cc 100644 --- a/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp +++ b/dom/canvas/WebGLExtensionCompressedTexturePVRTC.cpp @@ -7,15 +7,34 @@ #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif + namespace mozilla { WebGLExtensionCompressedTexturePVRTC::WebGLExtensionCompressedTexturePVRTC(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1); + RefPtr webgl_ = webgl; // Bug 1201275 + const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) { + auto& fua = webgl_->mFormatUsage; + + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; + fua->AllowSizedTexFormat(sizedFormat, usage); + + webgl_->mCompressedTextureFormats.AppendElement(sizedFormat); + }; + +#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x + + fnAdd(FOO(COMPRESSED_RGB_PVRTC_4BPPV1)); + fnAdd(FOO(COMPRESSED_RGB_PVRTC_2BPPV1)); + fnAdd(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1)); + fnAdd(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1)); + +#undef FOO } WebGLExtensionCompressedTexturePVRTC::~WebGLExtensionCompressedTexturePVRTC() diff --git a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp index 042385a98e..9ecf3de594 100644 --- a/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp +++ b/dom/canvas/WebGLExtensionCompressedTextureS3TC.cpp @@ -7,15 +7,34 @@ #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif + namespace mozilla { WebGLExtensionCompressedTextureS3TC::WebGLExtensionCompressedTextureS3TC(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT); - webgl->mCompressedTextureFormats.AppendElement(LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT); + RefPtr webgl_ = webgl; // Bug 1201275 + const auto fnAdd = [&webgl_](GLenum sizedFormat, webgl::EffectiveFormat effFormat) { + auto& fua = webgl_->mFormatUsage; + + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; + fua->AllowSizedTexFormat(sizedFormat, usage); + + webgl_->mCompressedTextureFormats.AppendElement(sizedFormat); + }; + +#define FOO(x) LOCAL_GL_ ## x, webgl::EffectiveFormat::x + + fnAdd(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT)); + fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT)); + fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT)); + fnAdd(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT)); + +#undef FOO } WebGLExtensionCompressedTextureS3TC::~WebGLExtensionCompressedTextureS3TC() diff --git a/dom/canvas/WebGLExtensionDepthTexture.cpp b/dom/canvas/WebGLExtensionDepthTexture.cpp index 00bf189353..bbcbe65143 100644 --- a/dom/canvas/WebGLExtensionDepthTexture.cpp +++ b/dom/canvas/WebGLExtensionDepthTexture.cpp @@ -13,6 +13,29 @@ namespace mozilla { WebGLExtensionDepthTexture::WebGLExtensionDepthTexture(WebGLContext* webgl) : WebGLExtensionBase(webgl) { + auto& fua = webgl->mFormatUsage; + + const auto fnAdd = [&fua](webgl::EffectiveFormat effFormat, GLenum unpackFormat, + GLenum unpackType) + { + auto usage = fua->EditUsage(effFormat); + usage->isRenderable = true; + + const webgl::PackingInfo pi = {unpackFormat, unpackType}; + const webgl::DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType}; + fua->AddTexUnpack(usage, pi, dui); + + fua->AllowUnsizedTexFormat(pi, usage); + }; + + fnAdd(webgl::EffectiveFormat::DEPTH_COMPONENT16, LOCAL_GL_DEPTH_COMPONENT, + LOCAL_GL_UNSIGNED_SHORT); + + fnAdd(webgl::EffectiveFormat::DEPTH_COMPONENT24, LOCAL_GL_DEPTH_COMPONENT, + LOCAL_GL_UNSIGNED_INT); + + fnAdd(webgl::EffectiveFormat::DEPTH24_STENCIL8, LOCAL_GL_DEPTH_STENCIL, + LOCAL_GL_UNSIGNED_INT_24_8); } WebGLExtensionDepthTexture::~WebGLExtensionDepthTexture() diff --git a/dom/canvas/WebGLExtensionDrawBuffers.cpp b/dom/canvas/WebGLExtensionDrawBuffers.cpp index 4be99124ac..b36e987c20 100644 --- a/dom/canvas/WebGLExtensionDrawBuffers.cpp +++ b/dom/canvas/WebGLExtensionDrawBuffers.cpp @@ -20,26 +20,12 @@ WebGLExtensionDrawBuffers::WebGLExtensionDrawBuffers(WebGLContext* webgl) { MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported."); - GLint maxColorAttachments = 0; - GLint maxDrawBuffers = 0; - - webgl->MakeContextCurrent(); - gl::GLContext* gl = webgl->GL(); - - gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments); - gl->fGetIntegerv(LOCAL_GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); - - // WEBGL_draw_buffers specifications don't give a maximal value reachable by MAX_COLOR_ATTACHMENTS. - maxColorAttachments = std::min(maxColorAttachments, GLint(WebGLContext::kMaxColorAttachments)); - - if (webgl->MinCapabilityMode()) - maxColorAttachments = std::min(maxColorAttachments, GLint(kMinColorAttachments)); - - // WEBGL_draw_buffers specifications request MAX_COLOR_ATTACHMENTS >= MAX_DRAW_BUFFERS. - maxDrawBuffers = std::min(maxDrawBuffers, GLint(maxColorAttachments)); - - webgl->mGLMaxColorAttachments = maxColorAttachments; - webgl->mGLMaxDrawBuffers = maxDrawBuffers; + // WEBGL_draw_buffers: + // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or + // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter." + webgl->mImplMaxColorAttachments = webgl->mGLMaxColorAttachments; + webgl->mImplMaxDrawBuffers = std::min(webgl->mGLMaxDrawBuffers, + webgl->mImplMaxColorAttachments); } WebGLExtensionDrawBuffers::~WebGLExtensionDrawBuffers() @@ -65,20 +51,12 @@ WebGLExtensionDrawBuffers::IsSupported(const WebGLContext* webgl) if (!gl->IsSupported(gl::GLFeature::draw_buffers)) return false; - GLint supportedColorAttachments = 0; - GLint supportedDrawBuffers = 0; - - webgl->MakeContextCurrent(); - - gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, &supportedColorAttachments); - gl->fGetIntegerv(LOCAL_GL_MAX_COLOR_ATTACHMENTS, &supportedDrawBuffers); - // WEBGL_draw_buffers requires at least 4 color attachments. - if (size_t(supportedColorAttachments) < kMinColorAttachments) - return false; - - if (size_t(supportedDrawBuffers) < kMinDrawBuffers) + if (webgl->mGLMaxDrawBuffers < webgl->kMinMaxDrawBuffers || + webgl->mGLMaxColorAttachments < webgl->kMinMaxColorAttachments) + { return false; + } return true; } diff --git a/dom/canvas/WebGLExtensionSRGB.cpp b/dom/canvas/WebGLExtensionSRGB.cpp index 0502ae0243..facb24f65f 100644 --- a/dom/canvas/WebGLExtensionSRGB.cpp +++ b/dom/canvas/WebGLExtensionSRGB.cpp @@ -12,9 +12,6 @@ namespace mozilla { -using mozilla::webgl::EffectiveFormat; - - WebGLExtensionSRGB::WebGLExtensionSRGB(WebGLContext* webgl) : WebGLExtensionBase(webgl) { @@ -28,20 +25,32 @@ WebGLExtensionSRGB::WebGLExtensionSRGB(WebGLContext* webgl) gl->fEnable(LOCAL_GL_FRAMEBUFFER_SRGB_EXT); } - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); + auto& fua = webgl->mFormatUsage; - auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat, - GLenum unpackFormat, GLenum unpackType, - bool asRenderbuffer) - { - if (!authority->GetUsage(effectiveFormat)) { - authority->AddFormat(effectiveFormat, asRenderbuffer, asRenderbuffer, true, true); - authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat); - } - }; + RefPtr gl_ = gl; // Bug 1201275 + const auto fnAdd = [&fua, &gl_](webgl::EffectiveFormat effFormat, GLenum format, + GLenum desktopUnpackFormat) + { + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; - addFormatIfMissing(EffectiveFormat::SRGB8 , LOCAL_GL_SRGB , LOCAL_GL_UNSIGNED_BYTE, false); - addFormatIfMissing(EffectiveFormat::SRGB8_ALPHA8, LOCAL_GL_SRGB_ALPHA, LOCAL_GL_UNSIGNED_BYTE, true); + webgl::DriverUnpackInfo dui = {format, format, LOCAL_GL_UNSIGNED_BYTE}; + const auto pi = dui.ToPacking(); + + if (!gl_->IsGLES()) + dui.unpackFormat = desktopUnpackFormat; + + fua->AddTexUnpack(usage, pi, dui); + + fua->AllowUnsizedTexFormat(pi, usage); + }; + + fnAdd(webgl::EffectiveFormat::SRGB8, LOCAL_GL_SRGB, LOCAL_GL_RGB); + fnAdd(webgl::EffectiveFormat::SRGB8_ALPHA8, LOCAL_GL_SRGB_ALPHA, LOCAL_GL_RGBA); + + auto usage = fua->EditUsage(webgl::EffectiveFormat::SRGB8_ALPHA8); + usage->isRenderable = true; + fua->AllowRBFormat(LOCAL_GL_SRGB8_ALPHA8, usage); } WebGLExtensionSRGB::~WebGLExtensionSRGB() diff --git a/dom/canvas/WebGLExtensionTextureFloat.cpp b/dom/canvas/WebGLExtensionTextureFloat.cpp index 50d7b0cd6e..a7a58939ad 100644 --- a/dom/canvas/WebGLExtensionTextureFloat.cpp +++ b/dom/canvas/WebGLExtensionTextureFloat.cpp @@ -4,63 +4,112 @@ #include "WebGLExtensions.h" +#include "GLContext.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" #include "WebGLFormats.h" namespace mozilla { -using mozilla::webgl::EffectiveFormat; - -void -WebGLExtensionTextureFloat::InitWebGLFormats(webgl::FormatUsageAuthority* authority) -{ - MOZ_ASSERT(authority); - - auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat) - { - if (!authority->GetUsage(effectiveFormat)) { - authority->AddFormat(effectiveFormat, false, false, false, false); - } - }; - - // Populate authority with any missing effective formats. - addFormatIfMissing(EffectiveFormat::RGBA32F); - addFormatIfMissing(EffectiveFormat::RGB32F); - addFormatIfMissing(EffectiveFormat::Luminance32FAlpha32F); - addFormatIfMissing(EffectiveFormat::Luminance32F); - addFormatIfMissing(EffectiveFormat::Alpha32F); -} - WebGLExtensionTextureFloat::WebGLExtensionTextureFloat(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); + MOZ_ASSERT(IsSupported(webgl)); - auto updateUsage = [authority](EffectiveFormat effectiveFormat, - GLenum unpackFormat, GLenum unpackType) - { - webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat); - MOZ_ASSERT(usage); - usage->asTexture = true; - authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat); - }; + auto& fua = webgl->mFormatUsage; + gl::GLContext* gl = webgl->GL(); - // Ensure require formats are initialized. - InitWebGLFormats(authority); + webgl::PackingInfo pi; + webgl::DriverUnpackInfo dui; + const GLint* swizzle = nullptr; - // Update usage to allow asTexture and add unpack - updateUsage(EffectiveFormat::RGBA32F , LOCAL_GL_RGBA , LOCAL_GL_FLOAT); - updateUsage(EffectiveFormat::RGB32F , LOCAL_GL_RGB , LOCAL_GL_FLOAT); - updateUsage(EffectiveFormat::Luminance32FAlpha32F, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT); - updateUsage(EffectiveFormat::Luminance32F , LOCAL_GL_LUMINANCE , LOCAL_GL_FLOAT); - updateUsage(EffectiveFormat::Alpha32F , LOCAL_GL_ALPHA , LOCAL_GL_FLOAT); + const auto fnAdd = [&fua, &pi, &dui, &swizzle](webgl::EffectiveFormat effFormat) + { + auto usage = fua->EditUsage(effFormat); + usage->textureSwizzleRGBA = swizzle; + fua->AddTexUnpack(usage, pi, dui); + + fua->AllowUnsizedTexFormat(pi, usage); + }; + + const bool needSizedInternal = !gl->IsGLES(); + MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle)); + + //////////////// + + pi = {LOCAL_GL_RGBA, LOCAL_GL_FLOAT}; + dui = {pi.format, pi.format, pi.type}; + swizzle = nullptr; + if (needSizedInternal) { + dui.internalFormat = LOCAL_GL_RGBA32F; + } + fnAdd(webgl::EffectiveFormat::RGBA32F); + + ////// + + pi = {LOCAL_GL_RGB, LOCAL_GL_FLOAT}; + dui = {pi.format, pi.format, pi.type}; + swizzle = nullptr; + if (needSizedInternal) { + dui.internalFormat = LOCAL_GL_RGB32F; + } + fnAdd(webgl::EffectiveFormat::RGB32F); + + ////// + + pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_FLOAT}; + dui = {pi.format, pi.format, pi.type}; + swizzle = nullptr; + if (needSizedInternal) { + dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT}; + swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA; + } + fnAdd(webgl::EffectiveFormat::Luminance32F); + + ////// + + pi = {LOCAL_GL_ALPHA, LOCAL_GL_FLOAT}; + dui = {pi.format, pi.format, pi.type}; + swizzle = nullptr; + if (needSizedInternal) { + dui = {LOCAL_GL_R32F, LOCAL_GL_RED, LOCAL_GL_FLOAT}; + swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA; + } + fnAdd(webgl::EffectiveFormat::Alpha32F); + + ////// + + pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT}; + dui = {pi.format, pi.format, pi.type}; + swizzle = nullptr; + if (needSizedInternal) { + dui = {LOCAL_GL_RG32F, LOCAL_GL_RG, LOCAL_GL_FLOAT}; + swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA; + } + fnAdd(webgl::EffectiveFormat::Luminance32FAlpha32F); } WebGLExtensionTextureFloat::~WebGLExtensionTextureFloat() { } +bool +WebGLExtensionTextureFloat::IsSupported(const WebGLContext* webgl) +{ + gl::GLContext* gl = webgl->GL(); + + if (!gl->IsSupported(gl::GLFeature::texture_float)) + return false; + + const bool needSizedInternal = !gl->IsGLES(); + const bool hasSwizzle = gl->IsSupported(gl::GLFeature::texture_swizzle); + + if (needSizedInternal && !hasSwizzle) + return false; + + return true; +} + IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureFloat, OES_texture_float) } // namespace mozilla diff --git a/dom/canvas/WebGLExtensionTextureFloatLinear.cpp b/dom/canvas/WebGLExtensionTextureFloatLinear.cpp index 1a9330c3c4..924113ee53 100644 --- a/dom/canvas/WebGLExtensionTextureFloatLinear.cpp +++ b/dom/canvas/WebGLExtensionTextureFloatLinear.cpp @@ -10,32 +10,16 @@ namespace mozilla { -using mozilla::webgl::EffectiveFormat; - WebGLExtensionTextureFloatLinear::WebGLExtensionTextureFloatLinear(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - // This update requires that the authority already be populated by - // WebGLExtensionTextureFloat. Enabling extensions to control - // features is a mess in WebGL + auto& fua = webgl->mFormatUsage; - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); - - auto updateUsage = [authority](EffectiveFormat effectiveFormat) { - webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat); - MOZ_ASSERT(usage); - usage->isFilterable = true; - }; - - // Ensure require formats are initialized. - WebGLExtensionTextureFloat::InitWebGLFormats(authority); - - // Update usage to allow isFilterable - updateUsage(EffectiveFormat::RGBA32F); - updateUsage(EffectiveFormat::RGB32F); - updateUsage(EffectiveFormat::Luminance32FAlpha32F); - updateUsage(EffectiveFormat::Luminance32F); - updateUsage(EffectiveFormat::Alpha32F); + fua->EditUsage(webgl::EffectiveFormat::RGBA32F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::RGB32F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::Luminance32FAlpha32F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::Luminance32F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::Alpha32F)->isFilterable = true; } WebGLExtensionTextureFloatLinear::~WebGLExtensionTextureFloatLinear() diff --git a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp index 044b60737a..a5770999d6 100644 --- a/dom/canvas/WebGLExtensionTextureHalfFloat.cpp +++ b/dom/canvas/WebGLExtensionTextureHalfFloat.cpp @@ -4,62 +4,119 @@ #include "WebGLExtensions.h" +#include "GLContext.h" #include "mozilla/dom/WebGLRenderingContextBinding.h" #include "WebGLContext.h" #include "WebGLFormats.h" namespace mozilla { -using mozilla::webgl::EffectiveFormat; - -void -WebGLExtensionTextureHalfFloat::InitWebGLFormats(webgl::FormatUsageAuthority* authority) -{ - MOZ_ASSERT(authority); - - auto addFormatIfMissing = [authority](EffectiveFormat effectiveFormat) - { - if (!authority->GetUsage(effectiveFormat)) { - authority->AddFormat(effectiveFormat, false, false, false, false); - } - }; - - // Populate authority with any missing effective formats. - addFormatIfMissing(EffectiveFormat::RGBA16F); - addFormatIfMissing(EffectiveFormat::RGB16F); - addFormatIfMissing(EffectiveFormat::Luminance16FAlpha16F); - addFormatIfMissing(EffectiveFormat::Luminance16F); - addFormatIfMissing(EffectiveFormat::Alpha16F); -} - WebGLExtensionTextureHalfFloat::WebGLExtensionTextureHalfFloat(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); + auto& fua = webgl->mFormatUsage; + gl::GLContext* gl = webgl->GL(); - auto updateUsage = [authority](EffectiveFormat effectiveFormat, - GLenum unpackFormat, GLenum unpackType) - { - webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat); - MOZ_ASSERT(usage); - usage->asTexture = true; - authority->AddUnpackOption(unpackFormat, unpackType, effectiveFormat); - }; + webgl::PackingInfo pi; + webgl::DriverUnpackInfo dui; + const GLint* swizzle = nullptr; - InitWebGLFormats(authority); + const auto fnAdd = [&fua, &pi, &dui, &swizzle](webgl::EffectiveFormat effFormat) + { + auto usage = fua->EditUsage(effFormat); + usage->textureSwizzleRGBA = swizzle; + fua->AddTexUnpack(usage, pi, dui); - // Update usage to allow asTexture and add unpack - updateUsage(EffectiveFormat::RGBA16F , LOCAL_GL_RGBA , LOCAL_GL_HALF_FLOAT_OES); - updateUsage(EffectiveFormat::RGB16F , LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES); - updateUsage(EffectiveFormat::Luminance16FAlpha16F, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES); - updateUsage(EffectiveFormat::Luminance16F , LOCAL_GL_LUMINANCE , LOCAL_GL_HALF_FLOAT_OES); - updateUsage(EffectiveFormat::Alpha16F , LOCAL_GL_ALPHA , LOCAL_GL_HALF_FLOAT_OES); + fua->AllowUnsizedTexFormat(pi, usage); + }; + + const bool needSizedInternal = !gl->IsGLES(); + MOZ_ASSERT_IF(needSizedInternal, gl->IsSupported(gl::GLFeature::texture_swizzle)); + + GLenum driverUnpackType = LOCAL_GL_HALF_FLOAT; + if (!gl->IsSupported(gl::GLFeature::texture_half_float)) { + MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float)); + driverUnpackType = LOCAL_GL_HALF_FLOAT_OES; + } + + //////////////// + + pi = {LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES}; + dui = {pi.format, pi.format, driverUnpackType}; + swizzle = nullptr; + if (needSizedInternal) { + dui.internalFormat = LOCAL_GL_RGBA16F; + } + fnAdd(webgl::EffectiveFormat::RGBA16F); + + ////// + + pi = {LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT_OES}; + dui = {pi.format, pi.format, driverUnpackType}; + swizzle = nullptr; + if (needSizedInternal) { + dui.internalFormat = LOCAL_GL_RGB16F; + } + fnAdd(webgl::EffectiveFormat::RGB16F); + + ////// + + pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_HALF_FLOAT_OES}; + dui = {pi.format, pi.format, driverUnpackType}; + swizzle = nullptr; + if (needSizedInternal) { + dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType}; + swizzle = webgl::FormatUsageInfo::kLuminanceSwizzleRGBA; + } + fnAdd(webgl::EffectiveFormat::Luminance16F); + + ////// + + pi = {LOCAL_GL_ALPHA, LOCAL_GL_HALF_FLOAT_OES}; + dui = {pi.format, pi.format, driverUnpackType}; + swizzle = nullptr; + if (needSizedInternal) { + dui = {LOCAL_GL_R16F, LOCAL_GL_RED, driverUnpackType}; + swizzle = webgl::FormatUsageInfo::kAlphaSwizzleRGBA; + } + fnAdd(webgl::EffectiveFormat::Alpha16F); + + ////// + + pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES}; + dui = {pi.format, pi.format, driverUnpackType}; + swizzle = nullptr; + if (needSizedInternal) { + dui = {LOCAL_GL_RG16F, LOCAL_GL_RG, driverUnpackType}; + swizzle = webgl::FormatUsageInfo::kLumAlphaSwizzleRGBA; + } + fnAdd(webgl::EffectiveFormat::Luminance16FAlpha16F); } WebGLExtensionTextureHalfFloat::~WebGLExtensionTextureHalfFloat() { } +bool +WebGLExtensionTextureHalfFloat::IsSupported(const WebGLContext* webgl) +{ + gl::GLContext* gl = webgl->GL(); + + if (!gl->IsSupported(gl::GLFeature::texture_half_float) && + !gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float)) + { + return false; + } + + const bool needSizedInternal = !gl->IsGLES(); + const bool hasSwizzle = gl->IsSupported(gl::GLFeature::texture_swizzle); + + if (needSizedInternal && !hasSwizzle) + return false; + + return true; +} + IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionTextureHalfFloat, OES_texture_half_float) } // namespace mozilla diff --git a/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp b/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp index 73949137eb..546938055c 100644 --- a/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp +++ b/dom/canvas/WebGLExtensionTextureHalfFloatLinear.cpp @@ -10,32 +10,16 @@ namespace mozilla { -using mozilla::webgl::EffectiveFormat; - WebGLExtensionTextureHalfFloatLinear::WebGLExtensionTextureHalfFloatLinear(WebGLContext* webgl) : WebGLExtensionBase(webgl) { - // This update requires that the authority already be populated by - // WebGLExtensionTextureHalfFloat. Enabling extensions to control - // features is a mess in WebGL + auto& fua = webgl->mFormatUsage; - webgl::FormatUsageAuthority* authority = webgl->mFormatUsage.get(); - - auto updateUsage = [authority](EffectiveFormat effectiveFormat) { - webgl::FormatUsageInfo* usage = authority->GetUsage(effectiveFormat); - MOZ_ASSERT(usage); - usage->isFilterable = true; - }; - - // Ensure require formats are initialized. - WebGLExtensionTextureHalfFloat::InitWebGLFormats(authority); - - // Update usage to allow isFilterable - updateUsage(EffectiveFormat::RGBA16F); - updateUsage(EffectiveFormat::RGB16F); - updateUsage(EffectiveFormat::Luminance16FAlpha16F); - updateUsage(EffectiveFormat::Luminance16F); - updateUsage(EffectiveFormat::Alpha16F); + fua->EditUsage(webgl::EffectiveFormat::RGBA16F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::RGB16F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::Luminance16FAlpha16F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::Luminance16F)->isFilterable = true; + fua->EditUsage(webgl::EffectiveFormat::Alpha16F)->isFilterable = true; } WebGLExtensionTextureHalfFloatLinear::~WebGLExtensionTextureHalfFloatLinear() diff --git a/dom/canvas/WebGLExtensions.h b/dom/canvas/WebGLExtensions.h index 4025ba541d..77d0e98e3f 100644 --- a/dom/canvas/WebGLExtensions.h +++ b/dom/canvas/WebGLExtensions.h @@ -38,7 +38,7 @@ public: explicit WebGLExtensionBase(WebGLContext* webgl); WebGLContext* GetParentObject() const { - return Context(); + return mContext; } void MarkLost(); @@ -219,6 +219,8 @@ public: explicit WebGLExtensionTextureFloat(WebGLContext*); virtual ~WebGLExtensionTextureFloat(); + static bool IsSupported(const WebGLContext*); + DECL_WEBGL_EXTENSION_GOOP }; @@ -241,6 +243,8 @@ public: explicit WebGLExtensionTextureHalfFloat(WebGLContext*); virtual ~WebGLExtensionTextureHalfFloat(); + static bool IsSupported(const WebGLContext*); + DECL_WEBGL_EXTENSION_GOOP }; @@ -289,14 +293,6 @@ public: static bool IsSupported(const WebGLContext*); - static const size_t kMinColorAttachments = 4; - static const size_t kMinDrawBuffers = 4; - /* - WEBGL_draw_buffers does not give a minal value for GL_MAX_DRAW_BUFFERS. But, we request - for GL_MAX_DRAW_BUFFERS = 4 at least to be able to use all requested color attachments. - See DrawBuffersWEBGL in WebGLExtensionDrawBuffers.cpp inner comments for more informations. - */ - DECL_WEBGL_EXTENSION_GOOP }; diff --git a/dom/canvas/WebGLFormats.cpp b/dom/canvas/WebGLFormats.cpp index 5966e80e4c..b7e748e911 100644 --- a/dom/canvas/WebGLFormats.cpp +++ b/dom/canvas/WebGLFormats.cpp @@ -5,43 +5,19 @@ #include "WebGLFormats.h" +#include "gfxPrefs.h" #include "GLDefs.h" #include "mozilla/StaticMutex.h" +#ifdef FOO +#error FOO is already defined! We use FOO() macros to keep things succinct in this file. +#endif + namespace mozilla { namespace webgl { -////////////////////////////////////////////////////////////////////////////////////////// - -std::map gCompressedFormatInfoMap; -std::map gFormatInfoMap; -std::map gUnpackTupleMap; -std::map gSizedFormatMap; - -static const CompressedFormatInfo* -GetCompressedFormatInfo(EffectiveFormat format) -{ - MOZ_ASSERT(!gCompressedFormatInfoMap.empty()); - auto itr = gCompressedFormatInfoMap.find(format); - if (itr == gCompressedFormatInfoMap.end()) - return nullptr; - - return &(itr->second); -} - -static const FormatInfo* -GetFormatInfo_NoLock(EffectiveFormat format) -{ - MOZ_ASSERT(!gFormatInfoMap.empty()); - auto itr = gFormatInfoMap.find(format); - if (itr == gFormatInfoMap.end()) - return nullptr; - - return &(itr->second); -} - template -static void +static inline void AlwaysInsert(std::map& dest, const K2& key, const V2& val) { auto res = dest.insert({ key, val }); @@ -49,12 +25,53 @@ AlwaysInsert(std::map& dest, const K2& key, const V2& val) MOZ_ALWAYS_TRUE(didInsert); } +template +static inline V* +FindOrNull(const std::map& dest, const K2& key) +{ + auto itr = dest.find(key); + if (itr == dest.end()) + return nullptr; + + return itr->second; +} + +// Returns a pointer to the in-place value for `key`. +template +static inline V* +FindPtrOrNull(const std::map& dest, const K2& key) +{ + auto itr = dest.find(key); + if (itr == dest.end()) + return nullptr; + + return &(itr->second); +} + +////////////////////////////////////////////////////////////////////////////////////////// + +std::map gCompressedFormatInfoMap; +std::map gFormatInfoMap; + +static inline const CompressedFormatInfo* +GetCompressedFormatInfo(EffectiveFormat format) +{ + MOZ_ASSERT(!gCompressedFormatInfoMap.empty()); + return FindPtrOrNull(gCompressedFormatInfoMap, format); +} + +static inline const FormatInfo* +GetFormatInfo_NoLock(EffectiveFormat format) +{ + MOZ_ASSERT(!gFormatInfoMap.empty()); + return FindPtrOrNull(gFormatInfoMap, format); +} + ////////////////////////////////////////////////////////////////////////////////////////// static void AddCompressedFormatInfo(EffectiveFormat format, uint16_t bitsPerBlock, uint8_t blockWidth, - uint8_t blockHeight, bool requirePOT, - SubImageUpdateBehavior subImageUpdateBehavior) + uint8_t blockHeight, CompressionFamily family) { MOZ_ASSERT(bitsPerBlock % 8 == 0); uint16_t bytesPerBlock = bitsPerBlock / 8; // The specs always state these in bits, @@ -63,7 +80,7 @@ AddCompressedFormatInfo(EffectiveFormat format, uint16_t bitsPerBlock, uint8_t b MOZ_ASSERT(bytesPerBlock <= 255); const CompressedFormatInfo info = { format, uint8_t(bytesPerBlock), blockWidth, - blockHeight, requirePOT, subImageUpdateBehavior }; + blockHeight, family }; AlwaysInsert(gCompressedFormatInfoMap, format, info); } @@ -72,45 +89,46 @@ InitCompressedFormatInfo() { // GLES 3.0.4, p147, table 3.19 // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats" - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_ETC2 , 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ETC2 , 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_R11_EAC , 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RG11_EAC , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_R11_EAC , 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC , 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 , 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_ETC2 , 64, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ETC2 , 64, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC , 128, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC , 128, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_R11_EAC , 64, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RG11_EAC , 128, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_R11_EAC , 64, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC , 128, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 , 64, 4, 4, CompressionFamily::ES3); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 64, 4, 4, CompressionFamily::ES3); // AMD_compressed_ATC_texture - AddCompressedFormatInfo(EffectiveFormat::ATC_RGB_AMD , 64, 4, 4, false, SubImageUpdateBehavior::Forbidden); - AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD , 128, 4, 4, false, SubImageUpdateBehavior::Forbidden); - AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD, 128, 4, 4, false, SubImageUpdateBehavior::Forbidden); + AddCompressedFormatInfo(EffectiveFormat::ATC_RGB_AMD , 64, 4, 4, CompressionFamily::ATC); + AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD , 128, 4, 4, CompressionFamily::ATC); + AddCompressedFormatInfo(EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD, 128, 4, 4, CompressionFamily::ATC); // EXT_texture_compression_s3tc - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1 , 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1, 64, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3, 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5, 128, 4, 4, false, SubImageUpdateBehavior::BlockAligned); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1_EXT , 64, 4, 4, CompressionFamily::S3TC); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1_EXT, 64, 4, 4, CompressionFamily::S3TC); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3_EXT, 128, 4, 4, CompressionFamily::S3TC); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5_EXT, 128, 4, 4, CompressionFamily::S3TC); // IMG_texture_compression_pvrtc - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1 , 256, 8, 8, true, SubImageUpdateBehavior::FullOnly); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1, 256, 8, 8, true, SubImageUpdateBehavior::FullOnly); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1 , 256, 16, 8, true, SubImageUpdateBehavior::FullOnly); - AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1, 256, 16, 8, true, SubImageUpdateBehavior::FullOnly); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1 , 256, 8, 8, CompressionFamily::PVRTC); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1, 256, 8, 8, CompressionFamily::PVRTC); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1 , 256, 16, 8, CompressionFamily::PVRTC); + AddCompressedFormatInfo(EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1, 256, 16, 8, CompressionFamily::PVRTC); // OES_compressed_ETC1_RGB8_texture - AddCompressedFormatInfo(EffectiveFormat::ETC1_RGB8, 64, 4, 4, false, SubImageUpdateBehavior::Forbidden); + AddCompressedFormatInfo(EffectiveFormat::ETC1_RGB8_OES, 64, 4, 4, CompressionFamily::ETC1); } ////////////////////////////////////////////////////////////////////////////////////////// static void -AddFormatInfo(EffectiveFormat format, const char* name, uint8_t bytesPerPixel, - UnsizedFormat unsizedFormat, ComponentType colorComponentType) +AddFormatInfo(EffectiveFormat format, const char* name, GLenum sizedFormat, + uint8_t bytesPerPixel, UnsizedFormat unsizedFormat, bool isSRGB, + ComponentType componentType) { - bool hasColor = false; + bool isColorFormat = false; bool hasAlpha = false; bool hasDepth = false; bool hasStencil = false; @@ -120,26 +138,29 @@ AddFormatInfo(EffectiveFormat format, const char* name, uint8_t bytesPerPixel, case UnsizedFormat::R: case UnsizedFormat::RG: case UnsizedFormat::RGB: - hasColor = true; - break; - case UnsizedFormat::A: - hasAlpha = true; + isColorFormat = true; break; + + case UnsizedFormat::A: // Alpha is a 'color format' since it's 'color-attachable'. case UnsizedFormat::LA: case UnsizedFormat::RGBA: - hasColor = true; + isColorFormat = true; hasAlpha = true; break; + case UnsizedFormat::D: hasDepth = true; break; + case UnsizedFormat::S: hasStencil = true; break; + case UnsizedFormat::DS: hasDepth = true; hasStencil = true; break; + default: MOZ_CRASH("Missing UnsizedFormat case."); } @@ -147,280 +168,137 @@ AddFormatInfo(EffectiveFormat format, const char* name, uint8_t bytesPerPixel, const CompressedFormatInfo* compressedFormatInfo = GetCompressedFormatInfo(format); MOZ_ASSERT(!bytesPerPixel == bool(compressedFormatInfo)); - const FormatInfo info = { format, name, unsizedFormat, colorComponentType, - bytesPerPixel, hasColor, hasAlpha, hasDepth, hasStencil, - compressedFormatInfo }; + const FormatInfo info = { format, name, sizedFormat, unsizedFormat, componentType, + bytesPerPixel, isColorFormat, isSRGB, hasAlpha, hasDepth, + hasStencil, compressedFormatInfo }; AlwaysInsert(gFormatInfoMap, format, info); } static void -InitFormatInfoMap() +InitFormatInfo() { -#ifdef FOO -#error FOO is already defined! -#endif - -#define FOO(x) EffectiveFormat::x, #x +#define FOO(x) EffectiveFormat::x, #x, LOCAL_GL_ ## x // GLES 3.0.4, p130-132, table 3.13 - AddFormatInfo(FOO(R8 ), 1, UnsizedFormat::R , ComponentType::NormUInt ); - AddFormatInfo(FOO(R8_SNORM ), 1, UnsizedFormat::R , ComponentType::NormInt ); - AddFormatInfo(FOO(RG8 ), 2, UnsizedFormat::RG , ComponentType::NormUInt ); - AddFormatInfo(FOO(RG8_SNORM ), 2, UnsizedFormat::RG , ComponentType::NormInt ); - AddFormatInfo(FOO(RGB8 ), 3, UnsizedFormat::RGB , ComponentType::NormUInt ); - AddFormatInfo(FOO(RGB8_SNORM ), 3, UnsizedFormat::RGB , ComponentType::NormInt ); - AddFormatInfo(FOO(RGB565 ), 2, UnsizedFormat::RGB , ComponentType::NormUInt ); - AddFormatInfo(FOO(RGBA4 ), 2, UnsizedFormat::RGBA, ComponentType::NormUInt ); - AddFormatInfo(FOO(RGB5_A1 ), 2, UnsizedFormat::RGBA, ComponentType::NormUInt ); - AddFormatInfo(FOO(RGBA8 ), 4, UnsizedFormat::RGBA, ComponentType::NormUInt ); - AddFormatInfo(FOO(RGBA8_SNORM ), 4, UnsizedFormat::RGBA, ComponentType::NormInt ); - AddFormatInfo(FOO(RGB10_A2 ), 4, UnsizedFormat::RGBA, ComponentType::NormUInt ); - AddFormatInfo(FOO(RGB10_A2UI ), 4, UnsizedFormat::RGBA, ComponentType::UInt ); + AddFormatInfo(FOO(R8 ), 1, UnsizedFormat::R , false, ComponentType::NormUInt); + AddFormatInfo(FOO(R8_SNORM ), 1, UnsizedFormat::R , false, ComponentType::NormInt ); + AddFormatInfo(FOO(RG8 ), 2, UnsizedFormat::RG , false, ComponentType::NormUInt); + AddFormatInfo(FOO(RG8_SNORM ), 2, UnsizedFormat::RG , false, ComponentType::NormInt ); + AddFormatInfo(FOO(RGB8 ), 3, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(RGB8_SNORM ), 3, UnsizedFormat::RGB , false, ComponentType::NormInt ); + AddFormatInfo(FOO(RGB565 ), 2, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(RGBA4 ), 2, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(RGB5_A1 ), 2, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(RGBA8 ), 4, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(RGBA8_SNORM ), 4, UnsizedFormat::RGBA, false, ComponentType::NormInt ); + AddFormatInfo(FOO(RGB10_A2 ), 4, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(RGB10_A2UI ), 4, UnsizedFormat::RGBA, false, ComponentType::UInt ); - AddFormatInfo(FOO(SRGB8 ), 3, UnsizedFormat::RGB , ComponentType::NormUIntSRGB); - AddFormatInfo(FOO(SRGB8_ALPHA8 ), 4, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB); + AddFormatInfo(FOO(SRGB8 ), 3, UnsizedFormat::RGB , true , ComponentType::NormUInt); + AddFormatInfo(FOO(SRGB8_ALPHA8 ), 4, UnsizedFormat::RGBA, true , ComponentType::NormUInt); - AddFormatInfo(FOO(R16F ), 2, UnsizedFormat::R , ComponentType::Float ); - AddFormatInfo(FOO(RG16F ), 4, UnsizedFormat::RG , ComponentType::Float ); - AddFormatInfo(FOO(RGB16F ), 6, UnsizedFormat::RGB , ComponentType::Float ); - AddFormatInfo(FOO(RGBA16F ), 8, UnsizedFormat::RGBA, ComponentType::Float ); - AddFormatInfo(FOO(R32F ), 4, UnsizedFormat::R , ComponentType::Float ); - AddFormatInfo(FOO(RG32F ), 8, UnsizedFormat::RG , ComponentType::Float ); - AddFormatInfo(FOO(RGB32F ), 12, UnsizedFormat::RGB , ComponentType::Float ); - AddFormatInfo(FOO(RGBA32F ), 16, UnsizedFormat::RGBA, ComponentType::Float ); + AddFormatInfo(FOO(R16F ), 2, UnsizedFormat::R , false, ComponentType::Float ); + AddFormatInfo(FOO(RG16F ), 4, UnsizedFormat::RG , false, ComponentType::Float ); + AddFormatInfo(FOO(RGB16F ), 6, UnsizedFormat::RGB , false, ComponentType::Float ); + AddFormatInfo(FOO(RGBA16F ), 8, UnsizedFormat::RGBA, false, ComponentType::Float ); + AddFormatInfo(FOO(R32F ), 4, UnsizedFormat::R , false, ComponentType::Float ); + AddFormatInfo(FOO(RG32F ), 8, UnsizedFormat::RG , false, ComponentType::Float ); + AddFormatInfo(FOO(RGB32F ), 12, UnsizedFormat::RGB , false, ComponentType::Float ); + AddFormatInfo(FOO(RGBA32F ), 16, UnsizedFormat::RGBA, false, ComponentType::Float ); - AddFormatInfo(FOO(R11F_G11F_B10F), 4, UnsizedFormat::RGB , ComponentType::Float ); - AddFormatInfo(FOO(RGB9_E5 ), 4, UnsizedFormat::RGB , ComponentType::SharedExp ); + AddFormatInfo(FOO(R11F_G11F_B10F), 4, UnsizedFormat::RGB , false, ComponentType::Float ); + AddFormatInfo(FOO(RGB9_E5 ), 4, UnsizedFormat::RGB , false, ComponentType::Float ); - AddFormatInfo(FOO(R8I ), 1, UnsizedFormat::R , ComponentType::Int ); - AddFormatInfo(FOO(R8UI ), 1, UnsizedFormat::R , ComponentType::UInt ); - AddFormatInfo(FOO(R16I ), 2, UnsizedFormat::R , ComponentType::Int ); - AddFormatInfo(FOO(R16UI ), 2, UnsizedFormat::R , ComponentType::UInt ); - AddFormatInfo(FOO(R32I ), 4, UnsizedFormat::R , ComponentType::Int ); - AddFormatInfo(FOO(R32UI ), 4, UnsizedFormat::R , ComponentType::UInt ); + AddFormatInfo(FOO(R8I ), 1, UnsizedFormat::R , false, ComponentType::Int ); + AddFormatInfo(FOO(R8UI ), 1, UnsizedFormat::R , false, ComponentType::UInt ); + AddFormatInfo(FOO(R16I ), 2, UnsizedFormat::R , false, ComponentType::Int ); + AddFormatInfo(FOO(R16UI ), 2, UnsizedFormat::R , false, ComponentType::UInt ); + AddFormatInfo(FOO(R32I ), 4, UnsizedFormat::R , false, ComponentType::Int ); + AddFormatInfo(FOO(R32UI ), 4, UnsizedFormat::R , false, ComponentType::UInt ); - AddFormatInfo(FOO(RG8I ), 2, UnsizedFormat::RG , ComponentType::Int ); - AddFormatInfo(FOO(RG8UI ), 2, UnsizedFormat::RG , ComponentType::UInt ); - AddFormatInfo(FOO(RG16I ), 4, UnsizedFormat::RG , ComponentType::Int ); - AddFormatInfo(FOO(RG16UI ), 4, UnsizedFormat::RG , ComponentType::UInt ); - AddFormatInfo(FOO(RG32I ), 8, UnsizedFormat::RG , ComponentType::Int ); - AddFormatInfo(FOO(RG32UI ), 8, UnsizedFormat::RG , ComponentType::UInt ); + AddFormatInfo(FOO(RG8I ), 2, UnsizedFormat::RG , false, ComponentType::Int ); + AddFormatInfo(FOO(RG8UI ), 2, UnsizedFormat::RG , false, ComponentType::UInt ); + AddFormatInfo(FOO(RG16I ), 4, UnsizedFormat::RG , false, ComponentType::Int ); + AddFormatInfo(FOO(RG16UI ), 4, UnsizedFormat::RG , false, ComponentType::UInt ); + AddFormatInfo(FOO(RG32I ), 8, UnsizedFormat::RG , false, ComponentType::Int ); + AddFormatInfo(FOO(RG32UI ), 8, UnsizedFormat::RG , false, ComponentType::UInt ); - AddFormatInfo(FOO(RGB8I ), 3, UnsizedFormat::RGB , ComponentType::Int ); - AddFormatInfo(FOO(RGB8UI ), 3, UnsizedFormat::RGB , ComponentType::UInt ); - AddFormatInfo(FOO(RGB16I ), 6, UnsizedFormat::RGB , ComponentType::Int ); - AddFormatInfo(FOO(RGB16UI ), 6, UnsizedFormat::RGB , ComponentType::UInt ); - AddFormatInfo(FOO(RGB32I ), 12, UnsizedFormat::RGB , ComponentType::Int ); - AddFormatInfo(FOO(RGB32UI ), 12, UnsizedFormat::RGB , ComponentType::UInt ); + AddFormatInfo(FOO(RGB8I ), 3, UnsizedFormat::RGB , false, ComponentType::Int ); + AddFormatInfo(FOO(RGB8UI ), 3, UnsizedFormat::RGB , false, ComponentType::UInt ); + AddFormatInfo(FOO(RGB16I ), 6, UnsizedFormat::RGB , false, ComponentType::Int ); + AddFormatInfo(FOO(RGB16UI ), 6, UnsizedFormat::RGB , false, ComponentType::UInt ); + AddFormatInfo(FOO(RGB32I ), 12, UnsizedFormat::RGB , false, ComponentType::Int ); + AddFormatInfo(FOO(RGB32UI ), 12, UnsizedFormat::RGB , false, ComponentType::UInt ); - AddFormatInfo(FOO(RGBA8I ), 4, UnsizedFormat::RGBA, ComponentType::Int ); - AddFormatInfo(FOO(RGBA8UI ), 4, UnsizedFormat::RGBA, ComponentType::UInt ); - AddFormatInfo(FOO(RGBA16I ), 8, UnsizedFormat::RGBA, ComponentType::Int ); - AddFormatInfo(FOO(RGBA16UI ), 8, UnsizedFormat::RGBA, ComponentType::UInt ); - AddFormatInfo(FOO(RGBA32I ), 16, UnsizedFormat::RGBA, ComponentType::Int ); - AddFormatInfo(FOO(RGBA32UI ), 16, UnsizedFormat::RGBA, ComponentType::UInt ); + AddFormatInfo(FOO(RGBA8I ), 4, UnsizedFormat::RGBA, false, ComponentType::Int ); + AddFormatInfo(FOO(RGBA8UI ), 4, UnsizedFormat::RGBA, false, ComponentType::UInt ); + AddFormatInfo(FOO(RGBA16I ), 8, UnsizedFormat::RGBA, false, ComponentType::Int ); + AddFormatInfo(FOO(RGBA16UI ), 8, UnsizedFormat::RGBA, false, ComponentType::UInt ); + AddFormatInfo(FOO(RGBA32I ), 16, UnsizedFormat::RGBA, false, ComponentType::Int ); + AddFormatInfo(FOO(RGBA32UI ), 16, UnsizedFormat::RGBA, false, ComponentType::UInt ); // GLES 3.0.4, p133, table 3.14 - AddFormatInfo(FOO(DEPTH_COMPONENT16 ), 2, UnsizedFormat::D , ComponentType::None); - AddFormatInfo(FOO(DEPTH_COMPONENT24 ), 3, UnsizedFormat::D , ComponentType::None); - AddFormatInfo(FOO(DEPTH_COMPONENT32F), 4, UnsizedFormat::D , ComponentType::None); - AddFormatInfo(FOO(DEPTH24_STENCIL8 ), 4, UnsizedFormat::DS, ComponentType::None); - AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, UnsizedFormat::DS, ComponentType::None); + AddFormatInfo(FOO(DEPTH_COMPONENT16 ), 2, UnsizedFormat::D , false, ComponentType::NormUInt); + AddFormatInfo(FOO(DEPTH_COMPONENT24 ), 3, UnsizedFormat::D , false, ComponentType::NormUInt); + AddFormatInfo(FOO(DEPTH_COMPONENT32F), 4, UnsizedFormat::D , false, ComponentType::Float); + AddFormatInfo(FOO(DEPTH24_STENCIL8 ), 4, UnsizedFormat::DS, false, ComponentType::Special); + AddFormatInfo(FOO(DEPTH32F_STENCIL8 ), 5, UnsizedFormat::DS, false, ComponentType::Special); // GLES 3.0.4, p205-206, "Required Renderbuffer Formats" - AddFormatInfo(FOO(STENCIL_INDEX8), 1, UnsizedFormat::S, ComponentType::None); - - // GLES 3.0.4, p128, table 3.12. - AddFormatInfo(FOO(Luminance8Alpha8), 2, UnsizedFormat::LA, ComponentType::NormUInt); - AddFormatInfo(FOO(Luminance8 ), 1, UnsizedFormat::L , ComponentType::NormUInt); - AddFormatInfo(FOO(Alpha8 ), 1, UnsizedFormat::A , ComponentType::None ); + AddFormatInfo(FOO(STENCIL_INDEX8), 1, UnsizedFormat::S, false, ComponentType::UInt); // GLES 3.0.4, p147, table 3.19 // GLES 3.0.4 p286+ $C.1 "ETC Compressed Texture Image Formats" - AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt ); - AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2 ), 0, UnsizedFormat::RGB , ComponentType::NormUIntSRGB); - AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt ); - AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC ), 0, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB); - AddFormatInfo(FOO(COMPRESSED_R11_EAC ), 0, UnsizedFormat::R , ComponentType::NormUInt ); - AddFormatInfo(FOO(COMPRESSED_RG11_EAC ), 0, UnsizedFormat::RG , ComponentType::NormUInt ); - AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC ), 0, UnsizedFormat::R , ComponentType::NormInt ); - AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC ), 0, UnsizedFormat::RG , ComponentType::NormInt ); - AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt ); - AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, UnsizedFormat::RGBA, ComponentType::NormUIntSRGB); + AddFormatInfo(FOO(COMPRESSED_RGB8_ETC2 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_SRGB8_ETC2 ), 0, UnsizedFormat::RGB , true , ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGBA8_ETC2_EAC ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC ), 0, UnsizedFormat::RGBA, true , ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_R11_EAC ), 0, UnsizedFormat::R , false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RG11_EAC ), 0, UnsizedFormat::RG , false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_SIGNED_R11_EAC ), 0, UnsizedFormat::R , false, ComponentType::NormInt ); + AddFormatInfo(FOO(COMPRESSED_SIGNED_RG11_EAC ), 0, UnsizedFormat::RG , false, ComponentType::NormInt ); + AddFormatInfo(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), 0, UnsizedFormat::RGBA, true , ComponentType::NormUInt); // AMD_compressed_ATC_texture - AddFormatInfo(FOO(ATC_RGB_AMD ), 0, UnsizedFormat::RGB , ComponentType::NormUInt); - AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD ), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); - AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); + AddFormatInfo(FOO(ATC_RGB_AMD ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(ATC_RGBA_EXPLICIT_ALPHA_AMD ), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(ATC_RGBA_INTERPOLATED_ALPHA_AMD), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); // EXT_texture_compression_s3tc - AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt); - AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); - AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); - AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGB_S3TC_DXT1_EXT ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT1_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT3_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGBA_S3TC_DXT5_EXT), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); // IMG_texture_compression_pvrtc - AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt); - AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); - AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, UnsizedFormat::RGB , ComponentType::NormUInt); - AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, UnsizedFormat::RGBA, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_4BPPV1 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_4BPPV1), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGB_PVRTC_2BPPV1 ), 0, UnsizedFormat::RGB , false, ComponentType::NormUInt); + AddFormatInfo(FOO(COMPRESSED_RGBA_PVRTC_2BPPV1), 0, UnsizedFormat::RGBA, false, ComponentType::NormUInt); // OES_compressed_ETC1_RGB8_texture - AddFormatInfo(FOO(ETC1_RGB8), 0, UnsizedFormat::RGB, ComponentType::NormUInt); - - // OES_texture_float - AddFormatInfo(FOO(Luminance32FAlpha32F), 2, UnsizedFormat::LA, ComponentType::Float); - AddFormatInfo(FOO(Luminance32F ), 1, UnsizedFormat::L , ComponentType::Float); - AddFormatInfo(FOO(Alpha32F ), 1, UnsizedFormat::A , ComponentType::Float); - - // OES_texture_half_float - AddFormatInfo(FOO(Luminance16FAlpha16F), 2, UnsizedFormat::LA, ComponentType::Float); - AddFormatInfo(FOO(Luminance16F ), 1, UnsizedFormat::L , ComponentType::Float); - AddFormatInfo(FOO(Alpha16F ), 1, UnsizedFormat::A , ComponentType::Float); + AddFormatInfo(FOO(ETC1_RGB8_OES), 0, UnsizedFormat::RGB, false, ComponentType::NormUInt); #undef FOO -} -////////////////////////////////////////////////////////////////////////////////////////// + // 'Virtual' effective formats have no sizedFormat. +#define FOO(x) EffectiveFormat::x, #x, 0 -static void -AddUnpackTuple(GLenum unpackFormat, GLenum unpackType, EffectiveFormat effectiveFormat) -{ - const UnpackTuple unpack = { unpackFormat, unpackType }; - const FormatInfo* info = GetFormatInfo_NoLock(effectiveFormat); - MOZ_ASSERT(info); + // GLES 3.0.4, p128, table 3.12. + AddFormatInfo(FOO(Luminance8Alpha8), 2, UnsizedFormat::LA, false, ComponentType::NormUInt); + AddFormatInfo(FOO(Luminance8 ), 1, UnsizedFormat::L , false, ComponentType::NormUInt); + AddFormatInfo(FOO(Alpha8 ), 1, UnsizedFormat::A , false, ComponentType::NormUInt); - AlwaysInsert(gUnpackTupleMap, unpack, info); -} + // OES_texture_float + AddFormatInfo(FOO(Luminance32FAlpha32F), 8, UnsizedFormat::LA, false, ComponentType::Float); + AddFormatInfo(FOO(Luminance32F ), 4, UnsizedFormat::L , false, ComponentType::Float); + AddFormatInfo(FOO(Alpha32F ), 4, UnsizedFormat::A , false, ComponentType::Float); -static void -InitUnpackTupleMap() -{ - AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGBA8 ); - AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4 ); - AddUnpackTuple(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1); - AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8 ); - AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5 , EffectiveFormat::RGB565 ); - - AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8); - AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8 ); - AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8 ); - - AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F ); - AddUnpackTuple(LOCAL_GL_RGBA , LOCAL_GL_FLOAT, EffectiveFormat::RGBA32F); - AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_FLOAT, EffectiveFormat::Luminance32FAlpha32F); - AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_FLOAT, EffectiveFormat::Luminance32F); - AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_FLOAT, EffectiveFormat::Alpha32F); - - AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGB16F ); - AddUnpackTuple(LOCAL_GL_RGBA , LOCAL_GL_HALF_FLOAT, EffectiveFormat::RGBA16F); - AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT, EffectiveFormat::Luminance16FAlpha16F); - AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_HALF_FLOAT, EffectiveFormat::Luminance16F); - AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_HALF_FLOAT, EffectiveFormat::Alpha16F); - - // Everyone's favorite problem-child: - AddUnpackTuple(LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F ); - AddUnpackTuple(LOCAL_GL_RGBA , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F); - AddUnpackTuple(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Luminance16FAlpha16F); - AddUnpackTuple(LOCAL_GL_LUMINANCE , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Luminance16F); - AddUnpackTuple(LOCAL_GL_ALPHA , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::Alpha16F); -} - -////////////////////////////////////////////////////////////////////////////////////////// - -static void -AddSizedFormat(GLenum sizedFormat, EffectiveFormat effectiveFormat) -{ - const FormatInfo* info = GetFormatInfo_NoLock(effectiveFormat); - MOZ_ASSERT(info); - - AlwaysInsert(gSizedFormatMap, sizedFormat, info); -} - -static void -InitSizedFormatMap() -{ - // GLES 3.0.4, p128-129 "Required Texture Formats" - - // "Texture and renderbuffer color formats" -#ifdef FOO -#error FOO is already defined! -#endif - -#define FOO(x) AddSizedFormat(LOCAL_GL_ ## x, EffectiveFormat::x) - - FOO(RGBA32I); - FOO(RGBA32UI); - FOO(RGBA16I); - FOO(RGBA16UI); - FOO(RGBA8); - FOO(RGBA8I); - FOO(RGBA8UI); - FOO(SRGB8_ALPHA8); - FOO(RGB10_A2); - FOO(RGB10_A2UI); - FOO(RGBA4); - FOO(RGB5_A1); - - FOO(RGB8); - FOO(RGB565); - - FOO(RG32I); - FOO(RG32UI); - FOO(RG16I); - FOO(RG16UI); - FOO(RG8); - FOO(RG8I); - FOO(RG8UI); - - FOO(R32I); - FOO(R32UI); - FOO(R16I); - FOO(R16UI); - FOO(R8); - FOO(R8I); - FOO(R8UI); - - // "Texture-only color formats" - FOO(RGBA32F); - FOO(RGBA16F); - FOO(RGBA8_SNORM); - - FOO(RGB32F); - FOO(RGB32I); - FOO(RGB32UI); - - FOO(RGB16F); - FOO(RGB16I); - FOO(RGB16UI); - - FOO(RGB8_SNORM); - FOO(RGB8I); - FOO(RGB8UI); - FOO(SRGB8); - - FOO(R11F_G11F_B10F); - FOO(RGB9_E5); - - FOO(RG32F); - FOO(RG16F); - FOO(RG8_SNORM); - - FOO(R32F); - FOO(R16F); - FOO(R8_SNORM); - - // "Depth formats" - FOO(DEPTH_COMPONENT32F); - FOO(DEPTH_COMPONENT24); - FOO(DEPTH_COMPONENT16); - - // "Combined depth+stencil formats" - FOO(DEPTH32F_STENCIL8); - FOO(DEPTH24_STENCIL8); - - // GLES 3.0.4, p205-206, "Required Renderbuffer Formats" - FOO(STENCIL_INDEX8); + // OES_texture_half_float + AddFormatInfo(FOO(Luminance16FAlpha16F), 4, UnsizedFormat::LA, false, ComponentType::Float); + AddFormatInfo(FOO(Luminance16F ), 2, UnsizedFormat::L , false, ComponentType::Float); + AddFormatInfo(FOO(Alpha16F ), 2, UnsizedFormat::A , false, ComponentType::Float); #undef FOO } @@ -430,7 +308,7 @@ InitSizedFormatMap() bool gAreFormatTablesInitialized = false; static void -EnsureInitFormatTables() +EnsureInitFormatTables(const StaticMutexAutoLock&) // Prove that you locked it! { if (MOZ_LIKELY(gAreFormatTablesInitialized)) return; @@ -438,9 +316,7 @@ EnsureInitFormatTables() gAreFormatTablesInitialized = true; InitCompressedFormatInfo(); - InitFormatInfoMap(); - InitUnpackTupleMap(); - InitSizedFormatMap(); + InitFormatInfo(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -449,54 +325,87 @@ EnsureInitFormatTables() StaticMutex gFormatMapMutex; const FormatInfo* -GetFormatInfo(EffectiveFormat format) +GetFormat(EffectiveFormat format) { StaticMutexAutoLock lock(gFormatMapMutex); - EnsureInitFormatTables(); + EnsureInitFormatTables(lock); return GetFormatInfo_NoLock(format); } -const FormatInfo* -GetInfoByUnpackTuple(GLenum unpackFormat, GLenum unpackType) -{ - StaticMutexAutoLock lock(gFormatMapMutex); - EnsureInitFormatTables(); - - const UnpackTuple unpack = { unpackFormat, unpackType }; - - MOZ_ASSERT(!gUnpackTupleMap.empty()); - auto itr = gUnpackTupleMap.find(unpack); - if (itr == gUnpackTupleMap.end()) - return nullptr; - - return itr->second; -} - -const FormatInfo* -GetInfoBySizedFormat(GLenum sizedFormat) -{ - StaticMutexAutoLock lock(gFormatMapMutex); - EnsureInitFormatTables(); - - MOZ_ASSERT(!gSizedFormatMap.empty()); - auto itr = gSizedFormatMap.find(sizedFormat); - if (itr == gSizedFormatMap.end()) - return nullptr; - - return itr->second; -} - ////////////////////////////////////////////////////////////////////////////////////////// -bool -FormatUsageInfo::CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const +uint8_t +BytesPerPixel(const PackingInfo& packing) { - const UnpackTuple key = { unpackFormat, unpackType }; - auto itr = validUnpacks.find(key); - return itr != validUnpacks.end(); + uint8_t bytesPerChannel; + switch (packing.type) { + case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: + case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: + case LOCAL_GL_UNSIGNED_SHORT_5_6_5: + return 2; + + case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: + case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: + case LOCAL_GL_UNSIGNED_INT_24_8: + case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: + return 4; + + case LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + return 8; + + // Alright, that's all the fixed-size unpackTypes. + + case LOCAL_GL_BYTE: + case LOCAL_GL_UNSIGNED_BYTE: + bytesPerChannel = 1; + break; + + case LOCAL_GL_SHORT: + case LOCAL_GL_UNSIGNED_SHORT: + case LOCAL_GL_HALF_FLOAT: + case LOCAL_GL_HALF_FLOAT_OES: + bytesPerChannel = 2; + break; + + case LOCAL_GL_INT: + case LOCAL_GL_UNSIGNED_INT: + case LOCAL_GL_FLOAT: + bytesPerChannel = 4; + break; + + default: + MOZ_CRASH("invalid PackingInfo"); + } + + uint8_t channels; + switch (packing.format) { + case LOCAL_GL_RG: + case LOCAL_GL_RG_INTEGER: + case LOCAL_GL_LUMINANCE_ALPHA: + channels = 2; + break; + + case LOCAL_GL_RGB: + case LOCAL_GL_RGB_INTEGER: + channels = 3; + break; + + case LOCAL_GL_RGBA: + case LOCAL_GL_RGBA_INTEGER: + channels = 4; + break; + + default: + channels = 1; + break; + } + + return bytesPerChannel * channels; } + + ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// @@ -507,621 +416,550 @@ FormatUsageInfo::CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const ////////////////////////////////////////////////////////////////////////////////////////// // FormatUsageAuthority + + +bool +FormatUsageInfo::IsUnpackValid(const PackingInfo& key, + const DriverUnpackInfo** const out_value) const +{ + auto itr = validUnpacks.find(key); + if (itr == validUnpacks.end()) + return false; + + *out_value = &(itr->second); + return true; +} + +//////////////////////////////////////// + +static void +AddSimpleUnsized(FormatUsageAuthority* fua, GLenum unpackFormat, GLenum unpackType, + EffectiveFormat effFormat) +{ + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; + + const PackingInfo pi = {unpackFormat, unpackType}; + const DriverUnpackInfo dui = {unpackFormat, unpackFormat, unpackType}; + fua->AddTexUnpack(usage, pi, dui); + + fua->AllowUnsizedTexFormat(pi, usage); +}; + + +/*static*/ const GLint FormatUsageInfo::kLuminanceSwizzleRGBA[4] = { LOCAL_GL_RED, + LOCAL_GL_RED, + LOCAL_GL_RED, + LOCAL_GL_ONE }; +/*static*/ const GLint FormatUsageInfo::kAlphaSwizzleRGBA[4] = { LOCAL_GL_ZERO, + LOCAL_GL_ZERO, + LOCAL_GL_ZERO, + LOCAL_GL_RED }; +/*static*/ const GLint FormatUsageInfo::kLumAlphaSwizzleRGBA[4] = { LOCAL_GL_RED, + LOCAL_GL_RED, + LOCAL_GL_RED, + LOCAL_GL_GREEN }; + +static bool +AddLegacyFormats_LA8(FormatUsageAuthority* fua, gl::GLContext* gl) +{ + if (gl->IsCoreProfile()) { + if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) + return false; + + PackingInfo pi; + DriverUnpackInfo dui; + + const auto fnAdd = [fua, &pi, &dui](EffectiveFormat effFormat, + const GLint* swizzle) + { + auto usage = fua->EditUsage(effFormat); + usage->isFilterable = true; + usage->textureSwizzleRGBA = swizzle; + + fua->AddTexUnpack(usage, pi, dui); + + fua->AllowUnsizedTexFormat(pi, usage); + }; + + pi = {LOCAL_GL_LUMINANCE, LOCAL_GL_UNSIGNED_BYTE}; + dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE}; + fnAdd(EffectiveFormat::Luminance8, FormatUsageInfo::kLuminanceSwizzleRGBA); + + pi = {LOCAL_GL_ALPHA, LOCAL_GL_UNSIGNED_BYTE}; + dui = {LOCAL_GL_R8, LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE}; + fnAdd(EffectiveFormat::Alpha8, FormatUsageInfo::kAlphaSwizzleRGBA); + + pi = {LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE}; + dui = {LOCAL_GL_RG8, LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE}; + fnAdd(EffectiveFormat::Luminance8Alpha8, FormatUsageInfo::kLumAlphaSwizzleRGBA); + } else { + AddSimpleUnsized(fua, LOCAL_GL_LUMINANCE , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8 ); + AddSimpleUnsized(fua, LOCAL_GL_ALPHA , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8 ); + AddSimpleUnsized(fua, LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8); + } + + return true; +} + +static bool +AddUnsizedFormats(FormatUsageAuthority* fua, gl::GLContext* gl) +{ + // GLES 2.0.25, p63, Table 3.4 + AddSimpleUnsized(fua, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGBA8 ); + AddSimpleUnsized(fua, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4 ); + AddSimpleUnsized(fua, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1); + AddSimpleUnsized(fua, LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8 ); + AddSimpleUnsized(fua, LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5 , EffectiveFormat::RGB565 ); + + // L, A, LA + return AddLegacyFormats_LA8(fua, gl); +} + UniquePtr -FormatUsageAuthority::CreateForWebGL1() +FormatUsageAuthority::CreateForWebGL1(gl::GLContext* gl) { UniquePtr ret(new FormatUsageAuthority); + const auto ptr = ret.get(); //////////////////////////////////////////////////////////////////////////// + // Usages + + const auto fnSet = [ptr](EffectiveFormat effFormat, bool isRenderable, + bool isFilterable) + { + MOZ_ASSERT(!ptr->GetUsage(effFormat)); + + auto usage = ptr->EditUsage(effFormat); + usage->isRenderable = isRenderable; + usage->isFilterable = isFilterable; + }; // GLES 2.0.25, p117, Table 4.5 // RGBA8 is made renderable in WebGL 1.0, "Framebuffer Object Attachments" + // render filter + // able able + fnSet(EffectiveFormat::RGBA8 , true , true); + fnSet(EffectiveFormat::RGBA4 , true , true); + fnSet(EffectiveFormat::RGB5_A1, true , true); + fnSet(EffectiveFormat::RGB8 , false, true); + fnSet(EffectiveFormat::RGB565 , true , true); - // render filter - // RB able Tex able - ret->AddFormat(EffectiveFormat::RGBA8 , false, true , true, true); - ret->AddFormat(EffectiveFormat::RGBA4 , true , true , true, true); - ret->AddFormat(EffectiveFormat::RGB5_A1, true , true , true, true); - ret->AddFormat(EffectiveFormat::RGB8 , false, false, true, true); - ret->AddFormat(EffectiveFormat::RGB565 , true , true , true, true); + fnSet(EffectiveFormat::Luminance8Alpha8, false, true); + fnSet(EffectiveFormat::Luminance8 , false, true); + fnSet(EffectiveFormat::Alpha8 , false, true); - ret->AddFormat(EffectiveFormat::Luminance8Alpha8, false, false, true, true); - ret->AddFormat(EffectiveFormat::Luminance8 , false, false, true, true); - ret->AddFormat(EffectiveFormat::Alpha8 , false, false, true, true); - - ret->AddFormat(EffectiveFormat::DEPTH_COMPONENT16, true, true, false, false); - ret->AddFormat(EffectiveFormat::STENCIL_INDEX8 , true, true, false, false); + fnSet(EffectiveFormat::DEPTH_COMPONENT16, true, false); + fnSet(EffectiveFormat::STENCIL_INDEX8 , true, false); // Added in WebGL 1.0 spec: - ret->AddFormat(EffectiveFormat::DEPTH24_STENCIL8, true, true, false, false); + fnSet(EffectiveFormat::DEPTH24_STENCIL8, true, false); + + //////////////////////////////////// + // RB formats + +#define FOO(x) ptr->AllowRBFormat(LOCAL_GL_ ## x, ptr->GetUsage(EffectiveFormat::x)) + + FOO(RGBA4 ); + FOO(RGB5_A1 ); + FOO(RGB565 ); + FOO(DEPTH_COMPONENT16); + FOO(STENCIL_INDEX8 ); + //FOO(DEPTH24_STENCIL8 ); // WebGL 1 uses DEPTH_STENCIL instead of DEPTH24_STENCIL8. + +#undef FOO + + ptr->AllowRBFormat(LOCAL_GL_DEPTH_STENCIL, + ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8)); //////////////////////////////////////////////////////////////////////////// - // GLES 2.0.25, p63, Table 3.4 - - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGBA8 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4, EffectiveFormat::RGBA4 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1, EffectiveFormat::RGB5_A1); - ret->AddUnpackOption(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8 ); - ret->AddUnpackOption(LOCAL_GL_RGB , LOCAL_GL_UNSIGNED_SHORT_5_6_5 , EffectiveFormat::RGB565 ); - - ret->AddUnpackOption(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8); - ret->AddUnpackOption(LOCAL_GL_LUMINANCE , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8 ); - ret->AddUnpackOption(LOCAL_GL_ALPHA , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8 ); - + if (!AddUnsizedFormats(ptr, gl)) + return nullptr; return Move(ret); } -static void -AddES3TexFormat(FormatUsageAuthority* that, EffectiveFormat format, bool isRenderable, - bool isFilterable) -{ - bool asRenderbuffer = isRenderable; - bool asTexture = true; - - that->AddFormat(format, asRenderbuffer, isRenderable, asTexture, isFilterable); -} - UniquePtr -FormatUsageAuthority::CreateForWebGL2() +FormatUsageAuthority::CreateForWebGL2(gl::GLContext* gl) { UniquePtr ret(new FormatUsageAuthority); - FormatUsageAuthority* const ptr = ret.get(); + const auto ptr = ret.get(); + + //////////////////////////////////////////////////////////////////////////// + // GLES 3.0.4 p111-113 + + const auto fnAddSizedUnpack = [ptr](EffectiveFormat effFormat, GLenum internalFormat, + GLenum unpackFormat, GLenum unpackType) + { + auto usage = ptr->EditUsage(effFormat); + + const PackingInfo pi = {unpackFormat, unpackType}; + const DriverUnpackInfo dui = {internalFormat, unpackFormat, unpackType}; + ptr->AddTexUnpack(usage, pi, dui); + }; + +#define FOO(x) EffectiveFormat::x, LOCAL_GL_ ## x + + // RGBA + fnAddSizedUnpack(FOO(RGBA8 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGBA4 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ); + fnAddSizedUnpack(FOO(RGBA4 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGB5_A1 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ); + fnAddSizedUnpack(FOO(RGB5_A1 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGB5_A1 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV); + fnAddSizedUnpack(FOO(SRGB8_ALPHA8), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGBA8_SNORM ), LOCAL_GL_RGBA, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(RGB10_A2 ), LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV); + fnAddSizedUnpack(FOO(RGBA16F ), LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT ); + fnAddSizedUnpack(FOO(RGBA16F ), LOCAL_GL_RGBA, LOCAL_GL_FLOAT ); + fnAddSizedUnpack(FOO(RGBA32F ), LOCAL_GL_RGBA, LOCAL_GL_FLOAT ); + + // RGBA_INTEGER + fnAddSizedUnpack(FOO(RGBA8UI ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGBA8I ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(RGBA16UI ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_SHORT ); + fnAddSizedUnpack(FOO(RGBA16I ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_SHORT ); + fnAddSizedUnpack(FOO(RGBA32UI ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT ); + fnAddSizedUnpack(FOO(RGBA32I ), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT ); + fnAddSizedUnpack(FOO(RGB10_A2UI), LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV); + + // RGB + fnAddSizedUnpack(FOO(RGB8 ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(SRGB8 ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGB565 ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5 ); + fnAddSizedUnpack(FOO(RGB565 ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGB8_SNORM ), LOCAL_GL_RGB, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV); + fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT ); + fnAddSizedUnpack(FOO(R11F_G11F_B10F), LOCAL_GL_RGB, LOCAL_GL_FLOAT ); + fnAddSizedUnpack(FOO(RGB16F ), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT ); + fnAddSizedUnpack(FOO(RGB16F ), LOCAL_GL_RGB, LOCAL_GL_FLOAT ); + fnAddSizedUnpack(FOO(RGB9_E5 ), LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV ); + fnAddSizedUnpack(FOO(RGB9_E5 ), LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT ); + fnAddSizedUnpack(FOO(RGB9_E5 ), LOCAL_GL_RGB, LOCAL_GL_FLOAT ); + fnAddSizedUnpack(FOO(RGB32F ), LOCAL_GL_RGB, LOCAL_GL_FLOAT ); + + // RGB_INTEGER + fnAddSizedUnpack(FOO(RGB8UI ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RGB8I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(RGB16UI), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_SHORT); + fnAddSizedUnpack(FOO(RGB16I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_SHORT ); + fnAddSizedUnpack(FOO(RGB32UI), LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_INT ); + fnAddSizedUnpack(FOO(RGB32I ), LOCAL_GL_RGB_INTEGER, LOCAL_GL_INT ); + + // RG + fnAddSizedUnpack(FOO(RG8 ), LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE); + fnAddSizedUnpack(FOO(RG8_SNORM), LOCAL_GL_RG, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(RG16F ), LOCAL_GL_RG, LOCAL_GL_HALF_FLOAT ); + fnAddSizedUnpack(FOO(RG16F ), LOCAL_GL_RG, LOCAL_GL_FLOAT ); + fnAddSizedUnpack(FOO(RG32F ), LOCAL_GL_RG, LOCAL_GL_FLOAT ); + + // RG_INTEGER + fnAddSizedUnpack(FOO(RG8UI ), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(RG8I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(RG16UI), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_SHORT); + fnAddSizedUnpack(FOO(RG16I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_SHORT ); + fnAddSizedUnpack(FOO(RG32UI), LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_INT ); + fnAddSizedUnpack(FOO(RG32I ), LOCAL_GL_RG_INTEGER, LOCAL_GL_INT ); + + // RED + fnAddSizedUnpack(FOO(R8 ), LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE); + fnAddSizedUnpack(FOO(R8_SNORM), LOCAL_GL_RED, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(R16F ), LOCAL_GL_RED, LOCAL_GL_HALF_FLOAT ); + fnAddSizedUnpack(FOO(R16F ), LOCAL_GL_RED, LOCAL_GL_FLOAT ); + fnAddSizedUnpack(FOO(R32F ), LOCAL_GL_RED, LOCAL_GL_FLOAT ); + + // RED_INTEGER + fnAddSizedUnpack(FOO(R8UI ), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_BYTE ); + fnAddSizedUnpack(FOO(R8I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_BYTE ); + fnAddSizedUnpack(FOO(R16UI), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_SHORT); + fnAddSizedUnpack(FOO(R16I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_SHORT ); + fnAddSizedUnpack(FOO(R32UI), LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_INT ); + fnAddSizedUnpack(FOO(R32I ), LOCAL_GL_RED_INTEGER, LOCAL_GL_INT ); + + // DEPTH_COMPONENT + fnAddSizedUnpack(FOO(DEPTH_COMPONENT16 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_SHORT); + fnAddSizedUnpack(FOO(DEPTH_COMPONENT16 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT ); + fnAddSizedUnpack(FOO(DEPTH_COMPONENT24 ), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT ); + fnAddSizedUnpack(FOO(DEPTH_COMPONENT32F), LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_FLOAT ); + + // DEPTH_STENCIL + fnAddSizedUnpack(FOO(DEPTH24_STENCIL8 ), LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_UNSIGNED_INT_24_8 ); + fnAddSizedUnpack(FOO(DEPTH32F_STENCIL8), LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV); + +#undef FOO //////////////////////////////////////////////////////////////////////////// // For renderable, see GLES 3.0.4, p212 "Framebuffer Completeness" // For filterable, see GLES 3.0.4, p161 "...a texture is complete unless..." + const auto fnAllowES3TexFormat = [ptr](GLenum sizedFormat, EffectiveFormat effFormat, + bool isRenderable, bool isFilterable) + { + auto usage = ptr->EditUsage(effFormat); + usage->isRenderable = isRenderable; + usage->isFilterable = isFilterable; + + ptr->AllowSizedTexFormat(sizedFormat, usage); + + if (isRenderable) { + ptr->AllowRBFormat(sizedFormat, usage); + } + }; + +#define FOO(x) LOCAL_GL_ ## x, EffectiveFormat::x + // GLES 3.0.4, p128-129 "Required Texture Formats" // GLES 3.0.4, p130-132, table 3.13 - // render filter - // able able - AddES3TexFormat(ptr, EffectiveFormat::R8 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::R8_SNORM , false, true ); - AddES3TexFormat(ptr, EffectiveFormat::RG8 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RG8_SNORM , false, true ); - AddES3TexFormat(ptr, EffectiveFormat::RGB8 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RGB8_SNORM , false, true ); - AddES3TexFormat(ptr, EffectiveFormat::RGB565 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RGBA4 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RGB5_A1 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RGBA8 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RGBA8_SNORM, false, true ); - AddES3TexFormat(ptr, EffectiveFormat::RGB10_A2 , true , true ); - AddES3TexFormat(ptr, EffectiveFormat::RGB10_A2UI , true , false); + // render filter + // able able + fnAllowES3TexFormat(FOO(R8 ), true , true ); + fnAllowES3TexFormat(FOO(R8_SNORM ), false, true ); + fnAllowES3TexFormat(FOO(RG8 ), true , true ); + fnAllowES3TexFormat(FOO(RG8_SNORM ), false, true ); + fnAllowES3TexFormat(FOO(RGB8 ), true , true ); + fnAllowES3TexFormat(FOO(RGB8_SNORM ), false, true ); + fnAllowES3TexFormat(FOO(RGB565 ), true , true ); + fnAllowES3TexFormat(FOO(RGBA4 ), true , true ); + fnAllowES3TexFormat(FOO(RGB5_A1 ), true , true ); + fnAllowES3TexFormat(FOO(RGBA8 ), true , true ); + fnAllowES3TexFormat(FOO(RGBA8_SNORM), false, true ); + fnAllowES3TexFormat(FOO(RGB10_A2 ), true , true ); + fnAllowES3TexFormat(FOO(RGB10_A2UI ), true , false); - AddES3TexFormat(ptr, EffectiveFormat::SRGB8 , false, true); - AddES3TexFormat(ptr, EffectiveFormat::SRGB8_ALPHA8, true , true); + fnAllowES3TexFormat(FOO(SRGB8 ), false, true); + fnAllowES3TexFormat(FOO(SRGB8_ALPHA8), true , true); - AddES3TexFormat(ptr, EffectiveFormat::R16F , false, true); - AddES3TexFormat(ptr, EffectiveFormat::RG16F , false, true); - AddES3TexFormat(ptr, EffectiveFormat::RGB16F , false, true); - AddES3TexFormat(ptr, EffectiveFormat::RGBA16F, false, true); + fnAllowES3TexFormat(FOO(R16F ), false, true); + fnAllowES3TexFormat(FOO(RG16F ), false, true); + fnAllowES3TexFormat(FOO(RGB16F ), false, true); + fnAllowES3TexFormat(FOO(RGBA16F), false, true); - AddES3TexFormat(ptr, EffectiveFormat::R32F , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RG32F , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB32F , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA32F, false, false); + fnAllowES3TexFormat(FOO(R32F ), false, false); + fnAllowES3TexFormat(FOO(RG32F ), false, false); + fnAllowES3TexFormat(FOO(RGB32F ), false, false); + fnAllowES3TexFormat(FOO(RGBA32F), false, false); - AddES3TexFormat(ptr, EffectiveFormat::R11F_G11F_B10F, false, true); - AddES3TexFormat(ptr, EffectiveFormat::RGB9_E5 , false, true); + fnAllowES3TexFormat(FOO(R11F_G11F_B10F), false, true); + fnAllowES3TexFormat(FOO(RGB9_E5 ), false, true); - AddES3TexFormat(ptr, EffectiveFormat::R8I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::R8UI , true, false); - AddES3TexFormat(ptr, EffectiveFormat::R16I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::R16UI, true, false); - AddES3TexFormat(ptr, EffectiveFormat::R32I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::R32UI, true, false); + fnAllowES3TexFormat(FOO(R8I ), true, false); + fnAllowES3TexFormat(FOO(R8UI ), true, false); + fnAllowES3TexFormat(FOO(R16I ), true, false); + fnAllowES3TexFormat(FOO(R16UI), true, false); + fnAllowES3TexFormat(FOO(R32I ), true, false); + fnAllowES3TexFormat(FOO(R32UI), true, false); - AddES3TexFormat(ptr, EffectiveFormat::RG8I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RG8UI , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RG16I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RG16UI, true, false); - AddES3TexFormat(ptr, EffectiveFormat::RG32I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RG32UI, true, false); + fnAllowES3TexFormat(FOO(RG8I ), true, false); + fnAllowES3TexFormat(FOO(RG8UI ), true, false); + fnAllowES3TexFormat(FOO(RG16I ), true, false); + fnAllowES3TexFormat(FOO(RG16UI), true, false); + fnAllowES3TexFormat(FOO(RG32I ), true, false); + fnAllowES3TexFormat(FOO(RG32UI), true, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB8I , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB8UI , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB16I , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB16UI, false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB32I , false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGB32UI, false, false); + fnAllowES3TexFormat(FOO(RGB8I ), false, false); + fnAllowES3TexFormat(FOO(RGB8UI ), false, false); + fnAllowES3TexFormat(FOO(RGB16I ), false, false); + fnAllowES3TexFormat(FOO(RGB16UI), false, false); + fnAllowES3TexFormat(FOO(RGB32I ), false, false); + fnAllowES3TexFormat(FOO(RGB32UI), false, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA8I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA8UI , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA16I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA16UI, true, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA32I , true, false); - AddES3TexFormat(ptr, EffectiveFormat::RGBA32UI, true, false); + fnAllowES3TexFormat(FOO(RGBA8I ), true, false); + fnAllowES3TexFormat(FOO(RGBA8UI ), true, false); + fnAllowES3TexFormat(FOO(RGBA16I ), true, false); + fnAllowES3TexFormat(FOO(RGBA16UI), true, false); + fnAllowES3TexFormat(FOO(RGBA32I ), true, false); + fnAllowES3TexFormat(FOO(RGBA32UI), true, false); // GLES 3.0.4, p133, table 3.14 - // GLES 3.0.4, p161 "...a texture is complete unless..." - AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT16 , true, false); - AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT24 , true, false); - AddES3TexFormat(ptr, EffectiveFormat::DEPTH_COMPONENT32F, true, false); - AddES3TexFormat(ptr, EffectiveFormat::DEPTH24_STENCIL8 , true, false); - AddES3TexFormat(ptr, EffectiveFormat::DEPTH32F_STENCIL8 , true, false); - - // GLES 3.0.4, p205-206, "Required Renderbuffer Formats" - AddES3TexFormat(ptr, EffectiveFormat::STENCIL_INDEX8, true, false); - - // GLES 3.0.4, p128, table 3.12. - // Unsized RGBA/RGB formats are renderable, other unsized are not. - AddES3TexFormat(ptr, EffectiveFormat::Luminance8Alpha8, false, true); - AddES3TexFormat(ptr, EffectiveFormat::Luminance8 , false, true); - AddES3TexFormat(ptr, EffectiveFormat::Alpha8 , false, true); + fnAllowES3TexFormat(FOO(DEPTH_COMPONENT16 ), true, false); + fnAllowES3TexFormat(FOO(DEPTH_COMPONENT24 ), true, false); + fnAllowES3TexFormat(FOO(DEPTH_COMPONENT32F), true, false); + fnAllowES3TexFormat(FOO(DEPTH24_STENCIL8 ), true, false); + fnAllowES3TexFormat(FOO(DEPTH32F_STENCIL8 ), true, false); // GLES 3.0.4, p147, table 3.19 // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats" - // (jgilbert) I can't find where these are established as filterable. - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGB8_ETC2 , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_ETC2 , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_R11_EAC , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RG11_EAC , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SIGNED_R11_EAC , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 , false, true); - AddES3TexFormat(ptr, EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, false, true); - //////////////////////////////////////////////////////////////////////////// - // GLES 3.0.4 p111-113 - // RGBA - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGBA8 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB5_A1 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGBA4 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::SRGB8_ALPHA8); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_BYTE , EffectiveFormat::RGBA8_SNORM ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 , EffectiveFormat::RGBA4 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 , EffectiveFormat::RGB5_A1 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB10_A2 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB5_A1 ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT , EffectiveFormat::RGBA16F ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_FLOAT , EffectiveFormat::RGBA32F ); - ret->AddUnpackOption(LOCAL_GL_RGBA, LOCAL_GL_FLOAT , EffectiveFormat::RGBA16F ); - // RGBA_INTEGER - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGBA8UI ); - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_BYTE , EffectiveFormat::RGBA8I ); - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_SHORT , EffectiveFormat::RGBA16UI ); - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_SHORT , EffectiveFormat::RGBA16I ); - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT , EffectiveFormat::RGBA32UI ); - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_INT , EffectiveFormat::RGBA32I ); - ret->AddUnpackOption(LOCAL_GL_RGBA_INTEGER, LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV, EffectiveFormat::RGB10_A2UI); + // Note that all compressed texture formats are filterable: + // GLES 3.0.4 p161: + // "[A] texture is complete unless any of the following conditions hold true: + // [...] + // * The effective internal format specified for the texture arrays is a sized + // internal color format that is not texture-filterable (see table 3.13) and [the + // mag filter requires filtering]." + // Compressed formats are not sized internal color formats, and indeed they are not + // listed in table 3.13. + fnAllowES3TexFormat(FOO(COMPRESSED_RGB8_ETC2 ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_ETC2 ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_RGBA8_ETC2_EAC ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_ALPHA8_ETC2_EAC ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_R11_EAC ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_RG11_EAC ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_SIGNED_R11_EAC ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_SIGNED_RG11_EAC ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 ), false, true); + fnAllowES3TexFormat(FOO(COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2), false, true); - // RGB - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8 ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB565 ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::SRGB8 ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_BYTE , EffectiveFormat::RGB8_SNORM ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_SHORT_5_6_5 , EffectiveFormat::RGB565 ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV, EffectiveFormat::R11F_G11F_B10F); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV , EffectiveFormat::RGB9_E5 ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT , EffectiveFormat::RGB16F ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT , EffectiveFormat::R11F_G11F_B10F); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_HALF_FLOAT , EffectiveFormat::RGB9_E5 ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT , EffectiveFormat::RGB32F ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT , EffectiveFormat::RGB16F ); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT , EffectiveFormat::R11F_G11F_B10F); - ret->AddUnpackOption(LOCAL_GL_RGB, LOCAL_GL_FLOAT , EffectiveFormat::RGB9_E5 ); +#undef FOO - // RGB_INTEGER - ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RGB8UI ); - ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_BYTE , EffectiveFormat::RGB8I ); - ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::RGB16UI); - ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_SHORT , EffectiveFormat::RGB16I ); - ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_UNSIGNED_INT , EffectiveFormat::RGB32UI); - ret->AddUnpackOption(LOCAL_GL_RGB_INTEGER, LOCAL_GL_INT , EffectiveFormat::RGB32I ); + // GLES 3.0.4, p206, "Required Renderbuffer Formats": + // "Implementations are also required to support STENCIL_INDEX8. Requesting this + // internal format for a renderbuffer will allocate at least 8 stencil bit planes." + auto usage = ptr->EditUsage(EffectiveFormat::STENCIL_INDEX8); + usage->isRenderable = true; + ptr->AllowRBFormat(LOCAL_GL_STENCIL_INDEX8, usage); - // RG - ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::RG8 ); - ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_BYTE , EffectiveFormat::RG8_SNORM); - ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_HALF_FLOAT , EffectiveFormat::RG16F ); - ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_FLOAT , EffectiveFormat::RG32F ); - ret->AddUnpackOption(LOCAL_GL_RG, LOCAL_GL_FLOAT , EffectiveFormat::RG16F ); + //////////////// + // Legacy formats - // RG_INTEGER - ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::RG8UI ); - ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_BYTE , EffectiveFormat::RG8I ); - ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::RG16UI); - ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_SHORT , EffectiveFormat::RG16I ); - ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_UNSIGNED_INT , EffectiveFormat::RG32UI); - ret->AddUnpackOption(LOCAL_GL_RG_INTEGER, LOCAL_GL_INT , EffectiveFormat::RG32I ); + if (!AddUnsizedFormats(ptr, gl)) + return nullptr; - // RED - ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::R8 ); - ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_BYTE , EffectiveFormat::R8_SNORM); - ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_HALF_FLOAT , EffectiveFormat::R16F ); - ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_FLOAT , EffectiveFormat::R32F ); - ret->AddUnpackOption(LOCAL_GL_RED, LOCAL_GL_FLOAT , EffectiveFormat::R16F ); + if (gfxPrefs::WebGL2CompatMode()) { + AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_FLOAT, EffectiveFormat::RGBA32F); + AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F ); - // RED_INTEGER - ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_BYTE , EffectiveFormat::R8UI ); - ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_BYTE , EffectiveFormat::R8I ); - ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::R16UI); - ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_SHORT , EffectiveFormat::R16I ); - ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_UNSIGNED_INT , EffectiveFormat::R32UI); - ret->AddUnpackOption(LOCAL_GL_RED_INTEGER, LOCAL_GL_INT , EffectiveFormat::R32I ); - - // DEPTH_COMPONENT - ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_SHORT, EffectiveFormat::DEPTH_COMPONENT16 ); - ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT , EffectiveFormat::DEPTH_COMPONENT24 ); - ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_UNSIGNED_INT , EffectiveFormat::DEPTH_COMPONENT16 ); - ret->AddUnpackOption(LOCAL_GL_DEPTH_COMPONENT, LOCAL_GL_FLOAT , EffectiveFormat::DEPTH_COMPONENT32F); - - // DEPTH_STENCIL - ret->AddUnpackOption(LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_UNSIGNED_INT_24_8 , EffectiveFormat::DEPTH24_STENCIL8 ); - ret->AddUnpackOption(LOCAL_GL_DEPTH_STENCIL, LOCAL_GL_FLOAT_32_UNSIGNED_INT_24_8_REV, EffectiveFormat::DEPTH32F_STENCIL8); - - // Unsized formats - ret->AddUnpackOption(LOCAL_GL_LUMINANCE_ALPHA, LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8Alpha8); - ret->AddUnpackOption(LOCAL_GL_LUMINANCE , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Luminance8 ); - ret->AddUnpackOption(LOCAL_GL_ALPHA , LOCAL_GL_UNSIGNED_BYTE, EffectiveFormat::Alpha8 ); + AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F); + AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F ); + } return Move(ret); } ////////////////////////////////////////////////////////////////////////////////////////// -FormatUsageInfo* -FormatUsageAuthority::GetUsage(EffectiveFormat format) +void +FormatUsageAuthority::AddTexUnpack(FormatUsageInfo* usage, const PackingInfo& pi, + const DriverUnpackInfo& dui) { - auto itr = mInfoMap.find(format); + // Don't AlwaysInsert here, since we'll see duplicates from sized and unsized formats. + auto res = usage->validUnpacks.insert({ pi, dui }); + auto itr = res.first; - if (itr == mInfoMap.end()) + if (!usage->idealUnpack) { + // First one! + usage->idealUnpack = &(itr->second); + } + + mValidTexUnpackFormats.insert(pi.format); + mValidTexUnpackTypes.insert(pi.type); +} + +static bool +Contains(const std::set& set, GLenum key) +{ + return set.find(key) != set.end(); +} + +bool +FormatUsageAuthority::IsInternalFormatEnumValid(GLenum internalFormat) const +{ + return Contains(mValidTexInternalFormats, internalFormat); +} + +bool +FormatUsageAuthority::AreUnpackEnumsValid(GLenum unpackFormat, GLenum unpackType) const +{ + return (Contains(mValidTexUnpackFormats, unpackFormat) && + Contains(mValidTexUnpackTypes, unpackType)); +} + +//////////////////// + +void +FormatUsageAuthority::AllowRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage) +{ + MOZ_ASSERT(!usage->format->compression); + MOZ_ASSERT(usage->format->sizedFormat); + MOZ_ASSERT(usage->isRenderable); + + AlwaysInsert(mRBFormatMap, sizedFormat, usage); +} + +void +FormatUsageAuthority::AllowSizedTexFormat(GLenum sizedFormat, + const FormatUsageInfo* usage) +{ + if (usage->format->compression) { + MOZ_ASSERT(usage->isFilterable, "Compressed formats should be filterable."); + } else { + MOZ_ASSERT(usage->validUnpacks.size() && usage->idealUnpack, + "AddTexUnpack() first."); + } + + AlwaysInsert(mSizedTexFormatMap, sizedFormat, usage); + + mValidTexInternalFormats.insert(sizedFormat); +} + +void +FormatUsageAuthority::AllowUnsizedTexFormat(const PackingInfo& pi, + const FormatUsageInfo* usage) +{ + MOZ_ASSERT(!usage->format->compression); + MOZ_ASSERT(usage->validUnpacks.size() && usage->idealUnpack, "AddTexUnpack() first."); + + AlwaysInsert(mUnsizedTexFormatMap, pi, usage); + + mValidTexInternalFormats.insert(pi.format); + mValidTexUnpackFormats.insert(pi.format); + mValidTexUnpackTypes.insert(pi.type); +} + +const FormatUsageInfo* +FormatUsageAuthority::GetRBUsage(GLenum sizedFormat) const +{ + return FindOrNull(mRBFormatMap, sizedFormat); +} + +const FormatUsageInfo* +FormatUsageAuthority::GetSizedTexUsage(GLenum sizedFormat) const +{ + return FindOrNull(mSizedTexFormatMap, sizedFormat); +} + +const FormatUsageInfo* +FormatUsageAuthority::GetUnsizedTexUsage(const PackingInfo& pi) const +{ + return FindOrNull(mUnsizedTexFormatMap, pi); +} + +FormatUsageInfo* +FormatUsageAuthority::EditUsage(EffectiveFormat format) +{ + auto itr = mUsageMap.find(format); + + if (itr == mUsageMap.end()) { + const FormatInfo* formatInfo = GetFormat(format); + MOZ_RELEASE_ASSERT(formatInfo); + + FormatUsageInfo usage(formatInfo); + + auto res = mUsageMap.insert({ format, usage }); + DebugOnly didInsert = res.second; + MOZ_ASSERT(didInsert); + + itr = res.first; + } + + return &(itr->second); +} + +const FormatUsageInfo* +FormatUsageAuthority::GetUsage(EffectiveFormat format) const +{ + auto itr = mUsageMap.find(format); + if (itr == mUsageMap.end()) return nullptr; return &(itr->second); } -void -FormatUsageAuthority::AddFormat(EffectiveFormat format, bool asRenderbuffer, - bool isRenderable, bool asTexture, bool isFilterable) -{ - MOZ_ASSERT_IF(asRenderbuffer, isRenderable); - MOZ_ASSERT_IF(isFilterable, asTexture); - - const FormatInfo* formatInfo = GetFormatInfo(format); - MOZ_RELEASE_ASSERT(formatInfo); - - FormatUsageInfo usage = { formatInfo, asRenderbuffer, isRenderable, asTexture, - isFilterable, std::set() }; - AlwaysInsert(mInfoMap, format, usage); -} - -void -FormatUsageAuthority::AddUnpackOption(GLenum unpackFormat, GLenum unpackType, - EffectiveFormat effectiveFormat) -{ - const UnpackTuple unpack = { unpackFormat, unpackType }; - FormatUsageInfo* usage = GetUsage(effectiveFormat); - MOZ_RELEASE_ASSERT(usage); - if (!usage) - return; - - MOZ_RELEASE_ASSERT(usage->asTexture); - - auto res = usage->validUnpacks.insert(unpack); - bool didInsert = res.second; - MOZ_ALWAYS_TRUE(didInsert); -} - //////////////////////////////////////////////////////////////////////////////// -struct ComponentSizes -{ - GLubyte redSize; - GLubyte greenSize; - GLubyte blueSize; - GLubyte alphaSize; - GLubyte depthSize; - GLubyte stencilSize; -}; - -static ComponentSizes kComponentSizes[] = { - // GLES 3.0.4, p128-129, "Required Texture Formats" - // "Texture and renderbuffer color formats" - { 32, 32, 32, 32, 0, 0 }, // RGBA32I, - { 32, 32, 32, 32, 0, 0 }, // RGBA32UI, - { 16, 16, 16, 16, 0, 0 }, // RGBA16I, - { 16, 16, 16, 16, 0, 0 }, // RGBA16UI, - { 8, 8, 8, 8, 0, 0 }, // RGBA8, - { 8, 8, 8, 8, 0, 0 }, // RGBA8I, - { 8, 8, 8, 8, 0, 0 }, // RGBA8UI, - { 8, 8, 8, 8, 0, 0 }, // SRGB8_ALPHA8, - { 10, 10, 10, 2, 0, 0 }, // RGB10_A2, - { 10, 10, 10, 2, 0, 0 }, // RGB10_A2UI, - { 4, 4, 4, 4, 0, 0 }, // RGBA4, - { 5, 5, 5, 1, 0, 0 }, // RGB5_A1, - - { 8, 8, 8, 0, 0, 0 }, // RGB8, - { 8, 8, 8, 0, 0, 0 }, // RGB565, - - { 32, 32, 0, 0, 0, 0 }, // RG32I, - { 32, 32, 0, 0, 0, 0 }, // RG32UI, - { 16, 16, 0, 0, 0, 0 }, // RG16I, - { 16, 16, 0, 0, 0, 0 }, // RG16UI, - { 8, 8, 0, 0, 0, 0 }, // RG8, - { 8, 8, 0, 0, 0, 0 }, // RG8I, - { 8, 8, 0, 0, 0, 0 }, // RG8UI, - - { 32, 0, 0, 0, 0, 0 }, // R32I, - { 32, 0, 0, 0, 0, 0 }, // R32UI, - { 16, 0, 0, 0, 0, 0 }, // R16I, - { 16, 0, 0, 0, 0, 0 }, // R16UI, - { 8, 0, 0, 0, 0, 0 }, // R8, - { 8, 0, 0, 0, 0, 0 }, // R8I, - { 8, 0, 0, 0, 0, 0 }, // R8UI, - - // "Texture-only color formats" - { 32, 32, 32, 32, 0, 0 }, // RGBA32F, - { 16, 16, 16, 16, 0, 0 }, // RGBA16F, - { 8, 8, 8, 8, 0, 0 }, // RGBA8_SNORM, - - { 32, 32, 32, 0, 0, 0 }, // RGB32F, - { 32, 32, 32, 0, 0, 0 }, // RGB32I, - { 32, 32, 32, 0, 0, 0 }, // RGB32UI, - - { 16, 16, 16, 0, 0, 0 }, // RGB16F, - { 16, 16, 16, 0, 0, 0 }, // RGB16I, - { 16, 16, 16, 0, 0, 0 }, // RGB16UI, - - { 8, 8, 8, 0, 0, 0 }, // RGB8_SNORM, - { 8, 8, 8, 0, 0, 0 }, // RGB8I, - { 8, 8, 8, 0, 0, 0 }, // RGB8UI, - { 8, 8, 8, 0, 0, 0 }, // SRGB8, - - { 11, 11, 11, 0, 0, 0 }, // R11F_G11F_B10F, - { 9, 9, 9, 0, 0, 0 }, // RGB9_E5, - - { 32, 32, 0, 0, 0, 0 }, // RG32F, - { 16, 16, 0, 0, 0, 0 }, // RG16F, - { 8, 8, 0, 0, 0, 0 }, // RG8_SNORM, - - { 32, 0, 0, 0, 0, 0 }, // R32F, - { 16, 0, 0, 0, 0, 0 }, // R16F, - { 8, 0, 0, 0, 0, 0 }, // R8_SNORM, - - // "Depth formats" - { 0, 0, 0, 0, 32, 0 }, // DEPTH_COMPONENT32F, - { 0, 0, 0, 0, 24, 0 }, // DEPTH_COMPONENT24, - { 0, 0, 0, 0, 16, 0 }, // DEPTH_COMPONENT16, - - // "Combined depth+stencil formats" - { 0, 0, 0, 0, 32, 8 }, // DEPTH32F_STENCIL0, - { 0, 0, 0, 0, 24, 8 }, // DEPTH24_STENCIL8, - - // GLES 3.0.4, p205-206, "Required Renderbuffer Formats" - { 0, 0, 0, 0, 0, 8 }, // STENCIL_INDEX8, - - // GLES 3.0.4, p128, table 3.12. - { 8, 8, 8, 8, 0, 0 }, // Luminance8Alpha8, - { 8, 8, 8, 0, 0, 0 }, // Luminance8, - { 0, 0, 0, 8, 0, 0 }, // Alpha8, - - // GLES 3.0.4, p147, table 3.19 - // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats" - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_R11_EAC, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SIGNED_R11_EAC, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RG11_EAC, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SIGNED_RG11_EAC, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGB8_ETC2, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SRGB8_ETC2, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA8_ETC2_EAC, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, - - // AMD_compressed_ATC_texture - { 8, 8, 8, 0, 0, 0 }, // ATC_RGB_AMD, - { 8, 8, 8, 8, 0, 0 }, // ATC_RGBA_EXPLICIT_ALPHA_AMD, - { 8, 8, 8, 8, 0, 0 }, // ATC_RGBA_INTERPOLATED_ALPHA_AMD, - - // EXT_texture_compression_s3tc - { 8, 8, 8, 0, 0, 0 }, // COMPRESSED_RGB_S3TC_DXT1, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_S3TC_DXT1, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_S3TC_DXT3, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_S3TC_DXT5, - - // IMG_texture_compression_pvrtc - { 8, 8, 8, 0, 0, 0 }, // COMPRESSED_RGB_PVRTC_4BPPV1, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_PVRTC_4BPPV1, - { 8, 8, 8, 0, 0, 0 }, // COMPRESSED_RGB_PVRTC_2BPPV1, - { 8, 8, 8, 8, 0, 0 }, // COMPRESSED_RGBA_PVRTC_2BPPV1, - - // OES_compressed_ETC1_RGB8_texture - { 8, 8, 8, 0, 0, 0 }, // ETC1_RGB8, - - // OES_texture_float - { 32, 32, 32, 32, 0, 0 }, // Luminance32FAlpha32F, - { 32, 32, 32, 0, 0, 0 }, // Luminance32F, - { 0, 0, 0, 32, 0, 0 }, // Alpha32F, - - // OES_texture_half_float - { 16, 16, 16, 16, 0, 0 }, // Luminance16FAlpha16F, - { 16, 16, 16, 0, 0, 0 }, // Luminance16F, - { 0, 0, 0, 16, 0, 0 }, // Alpha16F, - - { 0, } // MAX -}; - -GLint -GetComponentSize(EffectiveFormat format, GLenum component) -{ - ComponentSizes compSize = kComponentSizes[(int) format]; - switch (component) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: - case LOCAL_GL_RENDERBUFFER_RED_SIZE: - case LOCAL_GL_RED_BITS: - return compSize.redSize; - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: - case LOCAL_GL_RENDERBUFFER_GREEN_SIZE: - case LOCAL_GL_GREEN_BITS: - return compSize.greenSize; - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: - case LOCAL_GL_RENDERBUFFER_BLUE_SIZE: - case LOCAL_GL_BLUE_BITS: - return compSize.blueSize; - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: - case LOCAL_GL_RENDERBUFFER_ALPHA_SIZE: - case LOCAL_GL_ALPHA_BITS: - return compSize.alphaSize; - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: - case LOCAL_GL_RENDERBUFFER_DEPTH_SIZE: - case LOCAL_GL_DEPTH_BITS: - return compSize.depthSize; - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: - case LOCAL_GL_STENCIL_BITS: - return compSize.stencilSize; - } - - return 0; -} - -static GLenum kComponentTypes[] = { - // GLES 3.0.4, p128-129, "Required Texture Formats" - // "Texture and renderbuffer color formats" - LOCAL_GL_INT, // RGBA32I, - LOCAL_GL_UNSIGNED_INT, // RGBA32UI, - LOCAL_GL_INT, // RGBA16I, - LOCAL_GL_UNSIGNED_INT, // RGBA16UI, - LOCAL_GL_UNSIGNED_NORMALIZED, // RGBA8, - LOCAL_GL_INT, // RGBA8I, - LOCAL_GL_UNSIGNED_INT, // RGBA8UI, - LOCAL_GL_UNSIGNED_NORMALIZED, // SRGB8_ALPHA8, - LOCAL_GL_UNSIGNED_NORMALIZED, // RGB10_A2, - LOCAL_GL_UNSIGNED_INT, // RGB10_A2UI, - LOCAL_GL_UNSIGNED_NORMALIZED, // RGBA4, - LOCAL_GL_UNSIGNED_NORMALIZED, // RGB5_A1, - - LOCAL_GL_UNSIGNED_NORMALIZED, // RGB8, - LOCAL_GL_UNSIGNED_NORMALIZED, // RGB565, - - LOCAL_GL_INT, // RG32I, - LOCAL_GL_UNSIGNED_INT, // RG32UI, - LOCAL_GL_INT, // RG16I, - LOCAL_GL_UNSIGNED_INT, // RG16UI, - LOCAL_GL_UNSIGNED_NORMALIZED, // RG8, - LOCAL_GL_INT, // RG8I, - LOCAL_GL_UNSIGNED_INT, // RG8UI, - - LOCAL_GL_INT, // R32I, - LOCAL_GL_UNSIGNED_INT, // R32UI, - LOCAL_GL_INT, // R16I, - LOCAL_GL_UNSIGNED_INT, // R16UI, - LOCAL_GL_UNSIGNED_NORMALIZED, // R8, - LOCAL_GL_INT, // R8I, - LOCAL_GL_UNSIGNED_INT, // R8UI, - - // "Texture-only color formats" - LOCAL_GL_FLOAT, // RGBA32F, - LOCAL_GL_FLOAT, // RGBA16F, - LOCAL_GL_SIGNED_NORMALIZED, // RGBA8_SNORM, - - LOCAL_GL_FLOAT, // RGB32F, - LOCAL_GL_INT, // RGB32I, - LOCAL_GL_UNSIGNED_INT, // RGB32UI, - - LOCAL_GL_FLOAT, // RGB16F, - LOCAL_GL_INT, // RGB16I, - LOCAL_GL_UNSIGNED_INT, // RGB16UI, - - LOCAL_GL_SIGNED_NORMALIZED, // RGB8_SNORM, - LOCAL_GL_INT, // RGB8I, - LOCAL_GL_UNSIGNED_INT, // RGB8UI, - LOCAL_GL_UNSIGNED_NORMALIZED, // SRGB8, - - LOCAL_GL_FLOAT, // R11F_G11F_B10F, - LOCAL_GL_FLOAT, // RGB9_E5, - - LOCAL_GL_FLOAT, // RG32F, - LOCAL_GL_FLOAT, // RG16F, - LOCAL_GL_SIGNED_NORMALIZED, // RG8_SNORM, - - LOCAL_GL_FLOAT, // R32F, - LOCAL_GL_FLOAT, // R16F, - LOCAL_GL_SIGNED_NORMALIZED, // R8_SNORM, - - // "Depth formats" - LOCAL_GL_FLOAT, // DEPTH_COMPONENT32F, - LOCAL_GL_UNSIGNED_NORMALIZED, // DEPTH_COMPONENT24, - LOCAL_GL_UNSIGNED_NORMALIZED, // DEPTH_COMPONENT16, - - // "Combined depth+stencil formats" - LOCAL_GL_FLOAT, // DEPTH32F_STENCIL8, - LOCAL_GL_UNSIGNED_NORMALIZED, // DEPTH24_STENCIL8, - - // GLES 3.0.4, p205-206, "Required Renderbuffer Formats" - LOCAL_GL_UNSIGNED_NORMALIZED, // STENCIL_INDEX8, - - // GLES 3.0.4, p128, table 3.12. - LOCAL_GL_UNSIGNED_NORMALIZED, // Luminance8Alpha8, - LOCAL_GL_UNSIGNED_NORMALIZED, // Luminance8, - LOCAL_GL_UNSIGNED_NORMALIZED, // Alpha8, - - // GLES 3.0.4, p147, table 3.19 - // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats" - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_R11_EAC, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SIGNED_R11_EAC, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RG11_EAC, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SIGNED_RG11_EAC, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB8_ETC2, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SRGB8_ETC2, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA8_ETC2_EAC, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, - - // AMD_compressed_ATC_texture - LOCAL_GL_UNSIGNED_NORMALIZED, // ATC_RGB_AMD, - LOCAL_GL_UNSIGNED_NORMALIZED, // ATC_RGBA_EXPLICIT_ALPHA_AMD, - LOCAL_GL_UNSIGNED_NORMALIZED, // ATC_RGBA_INTERPOLATED_ALPHA_AMD, - - // EXT_texture_compression_s3tc - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB_S3TC_DXT1, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_S3TC_DXT1, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_S3TC_DXT3, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_S3TC_DXT5, - - // IMG_texture_compression_pvrtc - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB_PVRTC_4BPPV1, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_PVRTC_4BPPV1, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGB_PVRTC_2BPPV1, - LOCAL_GL_UNSIGNED_NORMALIZED, // COMPRESSED_RGBA_PVRTC_2BPPV1, - - // OES_compressed_ETC1_RGB8_texture - LOCAL_GL_UNSIGNED_NORMALIZED, // ETC1_RGB8, - - // OES_texture_float - LOCAL_GL_FLOAT, // Luminance32FAlpha32F, - LOCAL_GL_FLOAT, // Luminance32F, - LOCAL_GL_FLOAT, // Alpha32F, - - // OES_texture_half_float - LOCAL_GL_FLOAT, // Luminance16FAlpha16F, - LOCAL_GL_FLOAT, // Luminance16F, - LOCAL_GL_FLOAT, // Alpha16F, - - LOCAL_GL_NONE // MAX -}; - -GLenum -GetComponentType(EffectiveFormat format) -{ - return kComponentTypes[(int) format]; -} - -GLenum -GetColorEncoding(EffectiveFormat format) -{ - const bool isSRGB = (GetFormatInfo(format)->colorComponentType == - ComponentType::NormUIntSRGB); - return (isSRGB) ? LOCAL_GL_SRGB : LOCAL_GL_LINEAR; -} - } // namespace webgl } // namespace mozilla diff --git a/dom/canvas/WebGLFormats.h b/dom/canvas/WebGLFormats.h index e780e77b80..aabd856607 100644 --- a/dom/canvas/WebGLFormats.h +++ b/dom/canvas/WebGLFormats.h @@ -93,10 +93,7 @@ enum class EffectiveFormat : EffectiveFormatValueT { // GLES 3.0.4, p205-206, "Required Renderbuffer Formats" STENCIL_INDEX8, - // GLES 3.0.4, p128, table 3.12. - Luminance8Alpha8, - Luminance8, - Alpha8, + //////////////////////////////////// // GLES 3.0.4, p147, table 3.19 // GLES 3.0.4, p286+, $C.1 "ETC Compressed Texture Image Formats" @@ -117,10 +114,10 @@ enum class EffectiveFormat : EffectiveFormatValueT { ATC_RGBA_INTERPOLATED_ALPHA_AMD, // EXT_texture_compression_s3tc - COMPRESSED_RGB_S3TC_DXT1, - COMPRESSED_RGBA_S3TC_DXT1, - COMPRESSED_RGBA_S3TC_DXT3, - COMPRESSED_RGBA_S3TC_DXT5, + COMPRESSED_RGB_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT3_EXT, + COMPRESSED_RGBA_S3TC_DXT5_EXT, // IMG_texture_compression_pvrtc COMPRESSED_RGB_PVRTC_4BPPV1, @@ -129,7 +126,14 @@ enum class EffectiveFormat : EffectiveFormatValueT { COMPRESSED_RGBA_PVRTC_2BPPV1, // OES_compressed_ETC1_RGB8_texture - ETC1_RGB8, + ETC1_RGB8_OES, + + //////////////////////////////////// + + // GLES 3.0.4, p128, table 3.12. + Luminance8Alpha8, + Luminance8, + Alpha8, // OES_texture_float Luminance32FAlpha32F, @@ -157,116 +161,151 @@ enum class UnsizedFormat : uint8_t { DS, }; -// GLES 3.0.4 p114 Table 3.4 +// GLES 3.0.4 p114 Table 3.4, p240 enum class ComponentType : uint8_t { - None, // DEPTH_COMPONENT32F + None, Int, // RGBA32I - UInt, // RGBA32UI + UInt, // RGBA32UI, STENCIL_INDEX8 NormInt, // RGBA8_SNORM - NormUInt, // RGBA8 - NormUIntSRGB, // SRGB8_ALPHA8 + NormUInt, // RGBA8, DEPTH_COMPONENT16 Float, // RGBA32F - SharedExp, // RGB9_E5 + Special, // DEPTH24_STENCIL8 }; -enum class SubImageUpdateBehavior : uint8_t { - Forbidden, - FullOnly, - BlockAligned, +enum class CompressionFamily : uint8_t { + ETC1, + ES3, // ETC2 or EAC + ATC, + S3TC, + PVRTC, }; //////////////////////////////////////////////////////////////////////////////// -struct CompressedFormatInfo { +struct CompressedFormatInfo +{ const EffectiveFormat effectiveFormat; const uint8_t bytesPerBlock; const uint8_t blockWidth; const uint8_t blockHeight; - const bool requirePOT; - const SubImageUpdateBehavior subImageUpdateBehavior; + const CompressionFamily family; }; -struct FormatInfo { +struct FormatInfo +{ const EffectiveFormat effectiveFormat; const char* const name; + const GLenum sizedFormat; const UnsizedFormat unsizedFormat; - const ComponentType colorComponentType; - const uint8_t bytesPerPixel; // 0 iff `!!compression`. - const bool hasColor; - const bool hasAlpha; + const ComponentType componentType; + const uint8_t estimatedBytesPerPixel; // 0 iff `!!compression`. Only use this for + const bool isColorFormat; // memory usage estimation. Use + const bool isSRGB; // BytesPerPixel(packingFormat, packingType) for + const bool hasAlpha; // calculating pack/unpack byte count. const bool hasDepth; const bool hasStencil; const CompressedFormatInfo* const compression; }; -////////////////////////////////////////////////////////////////////////////////////////// +struct PackingInfo +{ + GLenum format; + GLenum type; -const FormatInfo* GetFormatInfo(EffectiveFormat format); -const FormatInfo* GetInfoByUnpackTuple(GLenum unpackFormat, GLenum unpackType); -const FormatInfo* GetInfoBySizedFormat(GLenum sizedFormat); - -//////////////////////////////////////// - -struct UnpackTuple { - const GLenum format; - const GLenum type; - - bool operator <(const UnpackTuple& x) const + bool operator <(const PackingInfo& x) const { - if (format == x.format) { - return type < x.type; - } + if (format != x.format) + return format < x.format; - return format < x.format; + return type < x.type; } }; -struct FormatUsageInfo { - const FormatInfo* const formatInfo; - bool asRenderbuffer; - bool isRenderable; - bool asTexture; - bool isFilterable; - std::set validUnpacks; +struct DriverUnpackInfo +{ + GLenum internalFormat; + GLenum unpackFormat; + GLenum unpackType; - bool CanUnpackWith(GLenum unpackFormat, GLenum unpackType) const; + PackingInfo ToPacking() const { + return {unpackFormat, unpackType}; + } +}; + +////////////////////////////////////////////////////////////////////////////////////////// + +const FormatInfo* GetFormat(EffectiveFormat format); +uint8_t BytesPerPixel(const PackingInfo& packing); +/* +GLint ComponentSize(const FormatInfo* format, GLenum component); +GLenum ComponentType(const FormatInfo* format); +*/ +//////////////////////////////////////// + +struct FormatUsageInfo +{ + const FormatInfo* const format; + bool isRenderable; + bool isFilterable; + std::map validUnpacks; + const DriverUnpackInfo* idealUnpack; + const GLint* textureSwizzleRGBA; + + static const GLint kLuminanceSwizzleRGBA[4]; + static const GLint kAlphaSwizzleRGBA[4]; + static const GLint kLumAlphaSwizzleRGBA[4]; + + explicit FormatUsageInfo(const FormatInfo* _format) + : format(_format) + , isRenderable(false) + , isFilterable(false) + , idealUnpack(nullptr) + , textureSwizzleRGBA(nullptr) + { } + + bool IsUnpackValid(const PackingInfo& key, + const DriverUnpackInfo** const out_value) const; }; class FormatUsageAuthority { - std::map mInfoMap; + std::map mUsageMap; + + std::map mRBFormatMap; + std::map mSizedTexFormatMap; + std::map mUnsizedTexFormatMap; + + std::set mValidTexInternalFormats; + std::set mValidTexUnpackFormats; + std::set mValidTexUnpackTypes; public: - static UniquePtr CreateForWebGL1(); - static UniquePtr CreateForWebGL2(); + static UniquePtr CreateForWebGL1(gl::GLContext* gl); + static UniquePtr CreateForWebGL2(gl::GLContext* gl); private: FormatUsageAuthority() { } public: - void AddFormat(EffectiveFormat format, bool asRenderbuffer, bool isRenderable, - bool asTexture, bool isFilterable); + FormatUsageInfo* EditUsage(EffectiveFormat format); + const FormatUsageInfo* GetUsage(EffectiveFormat format) const; - void AddUnpackOption(GLenum unpackFormat, GLenum unpackType, - EffectiveFormat effectiveFormat); + void AddTexUnpack(FormatUsageInfo* usage, const PackingInfo& pi, + const DriverUnpackInfo& dui); - FormatUsageInfo* GetUsage(EffectiveFormat format); - FormatUsageInfo* GetUsage(const FormatInfo* format) - { - if (!format) - return nullptr; + bool IsInternalFormatEnumValid(GLenum internalFormat) const; + bool AreUnpackEnumsValid(GLenum unpackFormat, GLenum unpackType) const; - return GetUsage(format->effectiveFormat); - } + void AllowRBFormat(GLenum sizedFormat, const FormatUsageInfo* usage); + void AllowSizedTexFormat(GLenum sizedFormat, const FormatUsageInfo* usage); + void AllowUnsizedTexFormat(const PackingInfo& pi, const FormatUsageInfo* usage); + + const FormatUsageInfo* GetRBUsage(GLenum sizedFormat) const; + const FormatUsageInfo* GetSizedTexUsage(GLenum sizedFormat) const; + const FormatUsageInfo* GetUnsizedTexUsage(const PackingInfo& pi) const; }; -//////////////////////////////////////////////////////////////////////////////// - -GLint GetComponentSize(EffectiveFormat format, GLenum component); -GLenum GetComponentType(EffectiveFormat format); -GLenum GetColorEncoding(EffectiveFormat format); - } // namespace webgl } // namespace mozilla diff --git a/dom/canvas/WebGLFramebuffer.cpp b/dom/canvas/WebGLFramebuffer.cpp index 93a8e29301..4c74e66ab6 100644 --- a/dom/canvas/WebGLFramebuffer.cpp +++ b/dom/canvas/WebGLFramebuffer.cpp @@ -15,8 +15,7 @@ namespace mozilla { -WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFramebuffer* fb, - FBAttachment attachmentPoint) +WebGLFBAttachPoint::WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint) : mFB(fb) , mAttachmentPoint(attachmentPoint) , mTexImageTarget(LOCAL_GL_NONE) @@ -28,6 +27,12 @@ WebGLFBAttachPoint::~WebGLFBAttachPoint() MOZ_ASSERT(!mTexturePtr); } +void +WebGLFBAttachPoint::Unlink() +{ + Clear(); +} + bool WebGLFBAttachPoint::IsDeleteRequested() const { @@ -39,85 +44,70 @@ WebGLFBAttachPoint::IsDeleteRequested() const bool WebGLFBAttachPoint::IsDefined() const { - return Renderbuffer() || - (Texture() && Texture()->HasImageInfoAt(ImageTarget(), 0)); + /* + return (Renderbuffer() && Renderbuffer()->IsDefined()) || + (Texture() && Texture()->ImageInfoAt(mTexImageTarget, + mTexImageLevel).IsDefined()); + */ + return (Renderbuffer() || Texture()); +} + +const webgl::FormatUsageInfo* +WebGLFBAttachPoint::Format() const +{ + MOZ_ASSERT(IsDefined()); + + if (Texture()) + return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).mFormat; + + if (Renderbuffer()) + return Renderbuffer()->Format(); + + return nullptr; } bool WebGLFBAttachPoint::HasAlpha() const { - MOZ_ASSERT(HasImage()); - - if (Texture() && - Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) - { - return FormatHasAlpha(Texture()->ImageInfoAt(mTexImageTarget, - mTexImageLevel).EffectiveInternalFormat()); - } - - if (Renderbuffer()) - return FormatHasAlpha(Renderbuffer()->InternalFormat()); - - return false; + return Format()->format->hasAlpha; } -GLenum +const webgl::FormatUsageInfo* WebGLFramebuffer::GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const { MOZ_ASSERT(attachment.IsDefined()); MOZ_ASSERT(attachment.Texture() || attachment.Renderbuffer()); - if (attachment.Texture()) { - const WebGLTexture& tex = *attachment.Texture(); - MOZ_ASSERT(tex.HasImageInfoAt(attachment.ImageTarget(), 0)); - - const WebGLTexture::ImageInfo& imgInfo = tex.ImageInfoAt(attachment.ImageTarget(), - 0); - return imgInfo.EffectiveInternalFormat().get(); - } - - if (attachment.Renderbuffer()) - return attachment.Renderbuffer()->InternalFormat(); - - return LOCAL_GL_NONE; -} - -TexInternalFormat -WebGLFBAttachPoint::EffectiveInternalFormat() const -{ - const WebGLTexture* tex = Texture(); - if (tex && tex->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) { - return tex->ImageInfoAt(mTexImageTarget, - mTexImageLevel).EffectiveInternalFormat(); - } - - const WebGLRenderbuffer* rb = Renderbuffer(); - if (rb) - return rb->InternalFormat(); - - return LOCAL_GL_NONE; + return attachment.Format(); } bool WebGLFBAttachPoint::IsReadableFloat() const { - TexInternalFormat internalformat = EffectiveInternalFormat(); - MOZ_ASSERT(internalformat != LOCAL_GL_NONE); - TexType type = TypeFromInternalFormat(internalformat); - return type == LOCAL_GL_FLOAT || - type == LOCAL_GL_HALF_FLOAT_OES || - type == LOCAL_GL_HALF_FLOAT; + auto formatUsage = Format(); + MOZ_ASSERT(formatUsage); + + auto format = formatUsage->format; + if (!format->isColorFormat) + return false; + + return format->componentType == webgl::ComponentType::Float; } -static void -UnmarkAttachment(WebGLFBAttachPoint& attachment) +void +WebGLFBAttachPoint::Clear() { - WebGLFramebufferAttachable* maybe = attachment.Texture(); - if (!maybe) - maybe = attachment.Renderbuffer(); + if (mRenderbufferPtr) { + MOZ_ASSERT(!mTexturePtr); + mRenderbufferPtr->UnmarkAttachment(*this); + } else if (mTexturePtr) { + mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).RemoveAttachPoint(this); + } - if (maybe) - maybe->UnmarkAttachment(attachment); + mTexturePtr = nullptr; + mRenderbufferPtr = nullptr; + + OnBackingStoreRespecified(); } void @@ -130,32 +120,28 @@ void WebGLFBAttachPoint::SetTexImageLayer(WebGLTexture* tex, TexImageTarget target, GLint level, GLint layer) { - mFB->InvalidateFramebufferStatus(); - - UnmarkAttachment(*this); + Clear(); mTexturePtr = tex; - mRenderbufferPtr = nullptr; mTexImageTarget = target; mTexImageLevel = level; mTexImageLayer = layer; - if (tex) - tex->MarkAttachment(*this); + if (mTexturePtr) { + mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel).AddAttachPoint(this); + } } void WebGLFBAttachPoint::SetRenderbuffer(WebGLRenderbuffer* rb) { - mFB->InvalidateFramebufferStatus(); + Clear(); - UnmarkAttachment(*this); - - mTexturePtr = nullptr; mRenderbufferPtr = rb; - if (rb) - rb->MarkAttachment(*this); + if (mRenderbufferPtr) { + mRenderbufferPtr->MarkAttachment(*this); + } } bool @@ -164,17 +150,15 @@ WebGLFBAttachPoint::HasUninitializedImageData() const if (!HasImage()) return false; - if (Renderbuffer()) - return Renderbuffer()->HasUninitializedImageData(); + if (mRenderbufferPtr) + return mRenderbufferPtr->HasUninitializedImageData(); - if (Texture()) { - MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); - return Texture()->ImageInfoAt(mTexImageTarget, - mTexImageLevel).HasUninitializedImageData(); - } + MOZ_ASSERT(mTexturePtr); - MOZ_ASSERT(false, "Should not get here."); - return false; + auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel); + MOZ_ASSERT(imageInfo.IsDefined()); + + return !imageInfo.IsDataInitialized(); } void @@ -183,195 +167,113 @@ WebGLFBAttachPoint::SetImageDataStatus(WebGLImageDataStatus newStatus) if (!HasImage()) return; - if (Renderbuffer()) { - Renderbuffer()->SetImageDataStatus(newStatus); + if (mRenderbufferPtr) { + mRenderbufferPtr->mImageDataStatus = newStatus; return; } - if (Texture()) { - Texture()->SetImageDataStatus(mTexImageTarget, mTexImageLevel, - newStatus); - return; - } + MOZ_ASSERT(mTexturePtr); - MOZ_ASSERT(false, "Should not get here."); + auto& imageInfo = mTexturePtr->ImageInfoAt(mTexImageTarget, mTexImageLevel); + MOZ_ASSERT(imageInfo.IsDefined()); + + const bool isDataInitialized = (newStatus == WebGLImageDataStatus::InitializedImageData); + imageInfo.SetIsDataInitialized(isDataInitialized, mTexturePtr); } bool WebGLFBAttachPoint::HasImage() const { - if (Texture() && Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)) + if (Texture() && Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined()) return true; - if (Renderbuffer()) + if (Renderbuffer() && Renderbuffer()->IsDefined()) return true; return false; } -const WebGLRectangleObject& -WebGLFBAttachPoint::RectangleObject() const -{ - MOZ_ASSERT(HasImage(), - "Make sure it has an image before requesting the rectangle."); - - if (Texture()) { - MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); - return Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); - } - - if (Renderbuffer()) - return *Renderbuffer(); - - MOZ_CRASH("Should not get here."); -} - -// The following IsValidFBOTextureXXX functions check the internal format that -// is used by GL or GL ES texture formats. This corresponds to the state that -// is stored in WebGLTexture::ImageInfo::InternalFormat() - -static inline bool -IsValidFBOTextureDepthFormat(GLenum internalformat) -{ - return IsGLDepthFormat(internalformat); -} - -static inline bool -IsValidFBOTextureDepthStencilFormat(GLenum internalformat) -{ - return IsGLDepthStencilFormat(internalformat); -} - -// The following IsValidFBORenderbufferXXX functions check the internal format -// that is stored by WebGLRenderbuffer::InternalFormat(). Valid values can be -// found in WebGLContext::RenderbufferStorage. - -static inline bool -IsValidFBORenderbufferDepthFormat(GLenum internalFormat) -{ - return internalFormat == LOCAL_GL_DEPTH_COMPONENT16; -} - -static inline bool -IsValidFBORenderbufferDepthStencilFormat(GLenum internalFormat) -{ - return internalFormat == LOCAL_GL_DEPTH_STENCIL; -} - -static inline bool -IsValidFBORenderbufferStencilFormat(GLenum internalFormat) -{ - return internalFormat == LOCAL_GL_STENCIL_INDEX8; -} - -bool -WebGLContext::IsFormatValidForFB(GLenum sizedFormat) const -{ - switch (sizedFormat) { - case LOCAL_GL_RGB8: - case LOCAL_GL_RGBA8: - case LOCAL_GL_RGB565: - case LOCAL_GL_RGB5_A1: - case LOCAL_GL_RGBA4: - return true; - - case LOCAL_GL_SRGB8: - case LOCAL_GL_SRGB8_ALPHA8_EXT: - return IsExtensionEnabled(WebGLExtensionID::EXT_sRGB); - - case LOCAL_GL_RGB32F: - case LOCAL_GL_RGBA32F: - return IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float); - - case LOCAL_GL_RGB16F: - case LOCAL_GL_RGBA16F: - return IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float); - } - - return false; -} - -bool -WebGLFBAttachPoint::IsComplete() const -{ - if (!HasImage()) - return false; - - const WebGLRectangleObject& rect = RectangleObject(); - - if (!rect.Width() || - !rect.Height()) - { - return false; - } - - if (Texture()) { - MOZ_ASSERT(Texture()->HasImageInfoAt(mTexImageTarget, mTexImageLevel)); - const WebGLTexture::ImageInfo& imageInfo = - Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); - GLenum sizedFormat = imageInfo.EffectiveInternalFormat().get(); - - if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) - return IsValidFBOTextureDepthFormat(sizedFormat); - - if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) { - // Textures can't have the correct format for stencil buffers. - return false; - } - - if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) - return IsValidFBOTextureDepthStencilFormat(sizedFormat); - - if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && - mAttachmentPoint <= FBAttachment(LOCAL_GL_COLOR_ATTACHMENT0 - 1 + - WebGLContext::kMaxColorAttachments)) - { - WebGLContext* webgl = Texture()->Context(); - return webgl->IsFormatValidForFB(sizedFormat); - } - MOZ_ASSERT(false, "Invalid WebGL attachment point?"); - return false; - } - - if (Renderbuffer()) { - GLenum internalFormat = Renderbuffer()->InternalFormat(); - - if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) - return IsValidFBORenderbufferDepthFormat(internalFormat); - - if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) - return IsValidFBORenderbufferStencilFormat(internalFormat); - - if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) - return IsValidFBORenderbufferDepthStencilFormat(internalFormat); - - if (mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0 && - mAttachmentPoint <= FBAttachment(LOCAL_GL_COLOR_ATTACHMENT0 - 1 + - WebGLContext::kMaxColorAttachments)) - { - WebGLContext* webgl = Renderbuffer()->Context(); - return webgl->IsFormatValidForFB(internalFormat); - } - MOZ_ASSERT(false, "Invalid WebGL attachment point?"); - return false; - } - MOZ_ASSERT(false, "Should not get here."); - return false; -} - void -WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, - FBAttachment attachmentLoc) const +WebGLFBAttachPoint::Size(uint32_t* const out_width, uint32_t* const out_height) const +{ + MOZ_ASSERT(HasImage()); + + if (Renderbuffer()) { + *out_width = Renderbuffer()->Width(); + *out_height = Renderbuffer()->Height(); + return; + } + + MOZ_ASSERT(Texture()); + MOZ_ASSERT(Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel).IsDefined()); + const auto& imageInfo = Texture()->ImageInfoAt(mTexImageTarget, mTexImageLevel); + + *out_width = imageInfo.mWidth; + *out_height = imageInfo.mHeight; +} + +void +WebGLFBAttachPoint::OnBackingStoreRespecified() const +{ + mFB->InvalidateFramebufferStatus(); +} + +bool +WebGLFBAttachPoint::IsComplete(WebGLContext* webgl) const +{ + MOZ_ASSERT(IsDefined()); + + if (!HasImage()) + return false; + + uint32_t width; + uint32_t height; + Size(&width, &height); + if (!width || !height) + return false; + + auto formatUsage = Format(); + if (!formatUsage->isRenderable) + return false; + + auto format = formatUsage->format; + + if (webgl->IsWebGL2()) { + if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) + return format->hasDepth; + + if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) + return format->hasStencil; + + if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) + MOZ_CRASH("No DEPTH_STENCIL_ATTACHMENT in WebGL 2."); + } else { + if (mAttachmentPoint == LOCAL_GL_DEPTH_ATTACHMENT) + return format->hasDepth && !format->hasStencil; + + if (mAttachmentPoint == LOCAL_GL_STENCIL_ATTACHMENT) + return !format->hasDepth && format->hasStencil; + + if (mAttachmentPoint == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) + return format->hasDepth && format->hasStencil; + } + + MOZ_ASSERT(mAttachmentPoint >= LOCAL_GL_COLOR_ATTACHMENT0); + return format->isColorFormat; +} + +void +WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, GLenum attachment) const { if (!HasImage()) { - switch (attachmentLoc.get()) { + switch (attachment) { case LOCAL_GL_DEPTH_ATTACHMENT: case LOCAL_GL_STENCIL_ATTACHMENT: case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: break; default: - gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachmentLoc.get(), + gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment, LOCAL_GL_RENDERBUFFER, 0); break; } @@ -381,7 +283,7 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, MOZ_ASSERT(HasImage()); if (Texture()) { - MOZ_ASSERT(gl == Texture()->Context()->GL()); + MOZ_ASSERT(gl == Texture()->mContext->GL()); const GLenum imageTarget = ImageTarget().get(); const GLint mipLevel = MipLevel(); @@ -396,29 +298,30 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: - if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, imageTarget, glName, mipLevel); - gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, - imageTarget, glName, mipLevel); + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, + LOCAL_GL_STENCIL_ATTACHMENT, imageTarget, + glName, mipLevel); } else { - gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachmentLoc.get(), - imageTarget, glName, mipLevel); + gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, attachment, imageTarget, + glName, mipLevel); } break; case LOCAL_GL_TEXTURE_2D_ARRAY: case LOCAL_GL_TEXTURE_3D: - if (attachmentLoc == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, - LOCAL_GL_DEPTH_ATTACHMENT, - glName, mipLevel, layer); + LOCAL_GL_DEPTH_ATTACHMENT, glName, mipLevel, + layer); gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, - LOCAL_GL_STENCIL_ATTACHMENT, - glName, mipLevel, layer); + LOCAL_GL_STENCIL_ATTACHMENT, glName, + mipLevel, layer); } else { - gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, attachmentLoc.get(), - glName, mipLevel, layer); + gl->fFramebufferTextureLayer(LOCAL_GL_FRAMEBUFFER, attachment, glName, + mipLevel, layer); } break; } @@ -426,7 +329,7 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, } if (Renderbuffer()) { - Renderbuffer()->FramebufferRenderbuffer(attachmentLoc); + Renderbuffer()->FramebufferRenderbuffer(attachment); return; } @@ -434,41 +337,95 @@ WebGLFBAttachPoint::FinalizeAttachment(gl::GLContext* gl, } JS::Value -WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname) +WebGLFBAttachPoint::GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx, + GLenum target, GLenum attachment, GLenum pname, + ErrorResult* const out_error) { - // TODO: WebGLTexture and WebGLRenderbuffer should store FormatInfo instead of doing - // this dance every time. - const GLenum internalFormat = EffectiveInternalFormat().get(); - const webgl::FormatInfo* info = webgl::GetInfoBySizedFormat(internalFormat); - MOZ_ASSERT(info); + const bool hasAttachment = (mTexturePtr || mRenderbufferPtr); + if (!hasAttachment) { + // Divergent between GLES 3 and 2. - WebGLTexture* tex = Texture(); + // GLES 2.0.25 p127: + // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, then querying any + // other pname will generate INVALID_ENUM." + // GLES 3.0.4 p240: + // "If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is NONE, no framebuffer is + // bound to target. In this case querying pname + // FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero, and all other queries + // will generate an INVALID_OPERATION error." + switch (pname) { + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return JS::Int32Value(LOCAL_GL_NONE); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + if (webgl->IsWebGL2()) + return JS::NullValue(); + + break; + + default: + break; + } + + if (webgl->IsWebGL2()) { + webgl->ErrorInvalidOperation("%s: No attachment at %s.", funcName, + webgl->EnumName(attachment)); + } else { + webgl->ErrorInvalidEnum("%s: No attachment at %s.", funcName, + webgl->EnumName(attachment)); + } + return JS::NullValue(); + } + + bool isPNameValid = false; switch (pname) { + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return JS::Int32Value(mTexturePtr ? LOCAL_GL_TEXTURE + : LOCAL_GL_RENDERBUFFER); + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return (mTexturePtr ? webgl->WebGLObjectAsJSValue(cx, mTexturePtr.get(), + *out_error) + : webgl->WebGLObjectAsJSValue(cx, mRenderbufferPtr.get(), + *out_error)); + //////////////// + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE: case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE: - return JS::Int32Value(webgl::GetComponentSize(info->effectiveFormat, pname)); + isPNameValid = true; + break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE: - return JS::Int32Value(webgl::GetComponentType(info->effectiveFormat)); + MOZ_ASSERT(attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); + isPNameValid = true; + break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING: - return JS::Int32Value(webgl::GetColorEncoding(info->effectiveFormat)); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: - if (tex) { - return JS::Int32Value(MipLevel()); + if (webgl->IsWebGL2() || + webgl->IsExtensionEnabled(WebGLExtensionID::EXT_sRGB)) + { + const auto format = Format()->format; + return JS::Int32Value(format->isSRGB ? LOCAL_GL_SRGB + : LOCAL_GL_LINEAR); } break; + //////////////// + + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: + if (mTexturePtr) + return JS::Int32Value(MipLevel()); + break; + case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: - if (tex) { - int32_t face = 0; - if (tex->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) { + if (mTexturePtr) { + GLenum face = 0; + if (mTexturePtr->Target() == LOCAL_GL_TEXTURE_CUBE_MAP) { face = ImageTarget().get(); } return JS::Int32Value(face); @@ -476,10 +433,10 @@ WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname) break; case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER: - if (tex) { + if (mTexturePtr) { int32_t layer = 0; - if (tex->Target() == LOCAL_GL_TEXTURE_2D_ARRAY || - tex->Target() == LOCAL_GL_TEXTURE_3D) + if (ImageTarget() == LOCAL_GL_TEXTURE_2D_ARRAY || + ImageTarget() == LOCAL_GL_TEXTURE_3D) { layer = Layer(); } @@ -488,9 +445,17 @@ WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname) break; } - context->ErrorInvalidEnum("getFramebufferParameter: Invalid combination of " - "attachment and pname."); - return JS::NullValue(); + if (!isPNameValid) { + webgl->ErrorInvalidEnum("%s: Invalid pname: 0x%04x", funcName, pname); + return JS::NullValue(); + } + + gl::GLContext* gl = webgl->GL(); + gl->MakeCurrent(); + + GLint ret = 0; + gl->fGetFramebufferAttachmentParameteriv(target, attachment, pname, &ret); + return JS::Int32Value(ret); } //////////////////////////////////////////////////////////////////////////////// @@ -499,16 +464,17 @@ WebGLFBAttachPoint::GetParameter(WebGLContext* context, GLenum pname) WebGLFramebuffer::WebGLFramebuffer(WebGLContext* webgl, GLuint fbo) : WebGLContextBoundObject(webgl) , mGLName(fbo) - , mStatus(0) + , mIsKnownFBComplete(false) , mReadBufferMode(LOCAL_GL_COLOR_ATTACHMENT0) , mColorAttachment0(this, LOCAL_GL_COLOR_ATTACHMENT0) , mDepthAttachment(this, LOCAL_GL_DEPTH_ATTACHMENT) , mStencilAttachment(this, LOCAL_GL_STENCIL_ATTACHMENT) , mDepthStencilAttachment(this, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) + , mMoreColorAttachments(webgl->mGLMaxColorAttachments) + , mDrawBuffers(1, LOCAL_GL_COLOR_ATTACHMENT0) #ifdef ANDROID , mIsFB(false) #endif - { mContext->mFramebuffers.insertBack(this); } @@ -521,13 +487,13 @@ WebGLFramebuffer::Delete() mStencilAttachment.Clear(); mDepthStencilAttachment.Clear(); - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - mMoreColorAttachments[i].Clear(); + for (auto& cur : mMoreColorAttachments) { + cur.Clear(); } mContext->MakeContextCurrent(); mContext->gl->fDeleteFramebuffers(1, &mGLName); + LinkedListElement::removeFrom(mContext->mFramebuffers); #ifdef ANDROID @@ -536,8 +502,7 @@ WebGLFramebuffer::Delete() } void -WebGLFramebuffer::FramebufferRenderbuffer(FBAttachment attachPointEnum, - RBTarget rbtarget, +WebGLFramebuffer::FramebufferRenderbuffer(GLenum attachment, RBTarget rbtarget, WebGLRenderbuffer* rb) { MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || @@ -546,16 +511,28 @@ WebGLFramebuffer::FramebufferRenderbuffer(FBAttachment attachPointEnum, if (!mContext->ValidateObjectAllowNull("framebufferRenderbuffer: renderbuffer", rb)) return; - // `attachPoint` is validated by ValidateFramebufferAttachment(). - WebGLFBAttachPoint& attachPoint = GetAttachPoint(attachPointEnum); - attachPoint.SetRenderbuffer(rb); + // `attachPointEnum` is validated by ValidateFramebufferAttachment(). + + RefPtr rb_ = rb; // Bug 1201275 + const auto fnAttach = [this, &rb_](GLenum attachment) { + const auto attachPoint = this->GetAttachPoint(attachment); + MOZ_ASSERT(attachPoint); + + attachPoint->SetRenderbuffer(rb_); + }; + + if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + fnAttach(LOCAL_GL_DEPTH_ATTACHMENT); + fnAttach(LOCAL_GL_STENCIL_ATTACHMENT); + } else { + fnAttach(attachment); + } InvalidateFramebufferStatus(); } void -WebGLFramebuffer::FramebufferTexture2D(FBAttachment attachPointEnum, - TexImageTarget texImageTarget, +WebGLFramebuffer::FramebufferTexture2D(GLenum attachment, TexImageTarget texImageTarget, WebGLTexture* tex, GLint level) { MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || @@ -574,56 +551,89 @@ WebGLFramebuffer::FramebufferTexture2D(FBAttachment attachPointEnum, } } - WebGLFBAttachPoint& attachPoint = GetAttachPoint(attachPointEnum); - attachPoint.SetTexImage(tex, texImageTarget, level); + RefPtr tex_ = tex; // Bug 1201275 + const auto fnAttach = [this, &tex_, texImageTarget, level](GLenum attachment) { + const auto attachPoint = this->GetAttachPoint(attachment); + MOZ_ASSERT(attachPoint); + + attachPoint->SetTexImage(tex_, texImageTarget, level); + }; + + if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + fnAttach(LOCAL_GL_DEPTH_ATTACHMENT); + fnAttach(LOCAL_GL_STENCIL_ATTACHMENT); + } else { + fnAttach(attachment); + } InvalidateFramebufferStatus(); } void -WebGLFramebuffer::FramebufferTextureLayer(FBAttachment attachment, WebGLTexture* tex, +WebGLFramebuffer::FramebufferTextureLayer(GLenum attachment, WebGLTexture* tex, GLint level, GLint layer) { MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || mContext->mBoundReadFramebuffer == this); - MOZ_ASSERT(tex); - WebGLFBAttachPoint& attachPoint = GetAttachPoint(attachment); - TexImageTarget texImageTarget = tex->Target(); - attachPoint.SetTexImageLayer(tex, texImageTarget, level, layer); + const TexImageTarget texImageTarget = (tex ? tex->Target().get() + : LOCAL_GL_TEXTURE_2D); + + RefPtr tex_ = tex; // Bug 1201275 + const auto fnAttach = [this, &tex_, texImageTarget, level, layer](GLenum attachment) { + const auto attachPoint = this->GetAttachPoint(attachment); + MOZ_ASSERT(attachPoint); + + attachPoint->SetTexImageLayer(tex_, texImageTarget, level, layer); + }; + + if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + fnAttach(LOCAL_GL_DEPTH_ATTACHMENT); + fnAttach(LOCAL_GL_STENCIL_ATTACHMENT); + } else { + fnAttach(attachment); + } InvalidateFramebufferStatus(); } -WebGLFBAttachPoint& -WebGLFramebuffer::GetAttachPoint(FBAttachment attachPoint) +WebGLFBAttachPoint* +WebGLFramebuffer::GetAttachPoint(GLenum attachPoint) { - switch (attachPoint.get()) { + switch (attachPoint) { case LOCAL_GL_COLOR_ATTACHMENT0: - return mColorAttachment0; + return &mColorAttachment0; case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - return mDepthStencilAttachment; + return &mDepthStencilAttachment; case LOCAL_GL_DEPTH_ATTACHMENT: - return mDepthAttachment; + return &mDepthAttachment; case LOCAL_GL_STENCIL_ATTACHMENT: - return mStencilAttachment; + return &mStencilAttachment; default: break; } - if (attachPoint >= LOCAL_GL_COLOR_ATTACHMENT1) { - size_t colorAttachmentId = attachPoint.get() - LOCAL_GL_COLOR_ATTACHMENT0; - if (colorAttachmentId < (size_t)mContext->mGLMaxColorAttachments) { - EnsureColorAttachPoints(colorAttachmentId); - return mMoreColorAttachments[colorAttachmentId - 1]; - } + const auto lastCAEnum = mContext->LastColorAttachmentEnum(); + if (attachPoint < LOCAL_GL_COLOR_ATTACHMENT1 || + attachPoint > lastCAEnum) + { + return nullptr; } - MOZ_CRASH("bad `attachPoint` validation"); + if (!mMoreColorAttachments.Size()) { + for (GLenum cur = LOCAL_GL_COLOR_ATTACHMENT1; cur <= lastCAEnum; cur++) { + mMoreColorAttachments.AppendNew(this, cur); + } + } + MOZ_ASSERT(LOCAL_GL_COLOR_ATTACHMENT0 + mMoreColorAttachments.Size() == lastCAEnum); + + const size_t offset = attachPoint - LOCAL_GL_COLOR_ATTACHMENT1; + MOZ_ASSERT(offset <= mMoreColorAttachments.Size()); + return &mMoreColorAttachments[offset]; } void @@ -641,10 +651,9 @@ WebGLFramebuffer::DetachTexture(const WebGLTexture* tex) if (mDepthStencilAttachment.Texture() == tex) mDepthStencilAttachment.Clear(); - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].Texture() == tex) - mMoreColorAttachments[i].Clear(); + for (auto& cur : mMoreColorAttachments) { + if (cur.Texture() == tex) + cur.Clear(); } } @@ -663,10 +672,9 @@ WebGLFramebuffer::DetachRenderbuffer(const WebGLRenderbuffer* rb) if (mDepthStencilAttachment.Renderbuffer() == rb) mDepthStencilAttachment.Clear(); - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].Renderbuffer() == rb) - mMoreColorAttachments[i].Clear(); + for (auto& cur : mMoreColorAttachments) { + if (cur.Renderbuffer() == rb) + cur.Clear(); } } @@ -680,69 +688,57 @@ WebGLFramebuffer::HasDefinedAttachments() const hasAttachments |= mStencilAttachment.IsDefined(); hasAttachments |= mDepthStencilAttachment.IsDefined(); - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - hasAttachments |= mMoreColorAttachments[i].IsDefined(); + for (const auto& cur : mMoreColorAttachments) { + hasAttachments |= cur.IsDefined(); } return hasAttachments; } -static bool -IsIncomplete(const WebGLFBAttachPoint& cur) -{ - return cur.IsDefined() && !cur.IsComplete(); -} - bool WebGLFramebuffer::HasIncompleteAttachments() const { + const auto fnIsIncomplete = [this](const WebGLFBAttachPoint& cur) { + if (!cur.IsDefined()) + return false; // Not defined, so can't count as incomplete. + + return !cur.IsComplete(this->mContext); + }; + bool hasIncomplete = false; - hasIncomplete |= IsIncomplete(mColorAttachment0); - hasIncomplete |= IsIncomplete(mDepthAttachment); - hasIncomplete |= IsIncomplete(mStencilAttachment); - hasIncomplete |= IsIncomplete(mDepthStencilAttachment); + hasIncomplete |= fnIsIncomplete(mColorAttachment0); + hasIncomplete |= fnIsIncomplete(mDepthAttachment); + hasIncomplete |= fnIsIncomplete(mStencilAttachment); + hasIncomplete |= fnIsIncomplete(mDepthStencilAttachment); - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - hasIncomplete |= IsIncomplete(mMoreColorAttachments[i]); + for (const auto& cur : mMoreColorAttachments) { + hasIncomplete |= fnIsIncomplete(cur); } return hasIncomplete; } -const WebGLRectangleObject& -WebGLFramebuffer::GetAnyRectObject() const +static bool +MatchOrReplaceSize(const WebGLFBAttachPoint& cur, uint32_t* const out_width, + uint32_t* const out_height) { - MOZ_ASSERT(HasDefinedAttachments()); + if (!cur.HasImage()) + return true; - if (mColorAttachment0.HasImage()) - return mColorAttachment0.RectangleObject(); + uint32_t width; + uint32_t height; + cur.Size(&width, &height); - if (mDepthAttachment.HasImage()) - return mDepthAttachment.RectangleObject(); - - if (mStencilAttachment.HasImage()) - return mStencilAttachment.RectangleObject(); - - if (mDepthStencilAttachment.HasImage()) - return mDepthStencilAttachment.RectangleObject(); - - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].HasImage()) - return mMoreColorAttachments[i].RectangleObject(); + if (!*out_width) { + MOZ_ASSERT(!*out_height); + *out_width = width; + *out_height = height; + return true; } - MOZ_CRASH("Should not get here."); -} - -static bool -RectsMatch(const WebGLFBAttachPoint& attachment, - const WebGLRectangleObject& rect) -{ - return attachment.RectangleObject().HasSameDimensionsAs(rect); + return (width == *out_width && + height == *out_height); } bool @@ -751,41 +747,22 @@ WebGLFramebuffer::AllImageRectsMatch() const MOZ_ASSERT(HasDefinedAttachments()); MOZ_ASSERT(!HasIncompleteAttachments()); - const WebGLRectangleObject& rect = GetAnyRectObject(); - - // Alright, we have *a* rect, let's check all the others. + uint32_t width = 0; + uint32_t height = 0; bool imageRectsMatch = true; - if (mColorAttachment0.HasImage()) - imageRectsMatch &= RectsMatch(mColorAttachment0, rect); + imageRectsMatch &= MatchOrReplaceSize(mColorAttachment0, &width, &height); + imageRectsMatch &= MatchOrReplaceSize(mDepthAttachment, &width, &height); + imageRectsMatch &= MatchOrReplaceSize(mStencilAttachment, &width, &height); + imageRectsMatch &= MatchOrReplaceSize(mDepthStencilAttachment, &width, &height); - if (mDepthAttachment.HasImage()) - imageRectsMatch &= RectsMatch(mDepthAttachment, rect); - - if (mStencilAttachment.HasImage()) - imageRectsMatch &= RectsMatch(mStencilAttachment, rect); - - if (mDepthStencilAttachment.HasImage()) - imageRectsMatch &= RectsMatch(mDepthStencilAttachment, rect); - - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].HasImage()) - imageRectsMatch &= RectsMatch(mMoreColorAttachments[i], rect); + for (const auto& cur : mMoreColorAttachments) { + imageRectsMatch &= MatchOrReplaceSize(cur, &width, &height); } return imageRectsMatch; } -const WebGLRectangleObject& -WebGLFramebuffer::RectangleObject() const -{ - // If we're using this as the RectObj of an FB, we need to be sure the FB - // has a consistent rect. - MOZ_ASSERT(AllImageRectsMatch(), "Did you mean `GetAnyRectObject`?"); - return GetAnyRectObject(); -} - FBStatus WebGLFramebuffer::PrecheckFramebufferStatus() const { @@ -798,11 +775,17 @@ WebGLFramebuffer::PrecheckFramebufferStatus() const if (HasIncompleteAttachments()) return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - if (!AllImageRectsMatch()) - return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes + if (!mContext->IsWebGL2()) { + // INCOMPLETE_DIMENSIONS doesn't exist in GLES3. + if (!AllImageRectsMatch()) + return LOCAL_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; // Inconsistent sizes - if (HasDepthStencilConflict()) - return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; + const auto depthOrStencilCount = int(mDepthAttachment.IsDefined()) + + int(mStencilAttachment.IsDefined()) + + int(mDepthStencilAttachment.IsDefined()); + if (depthOrStencilCount > 1) + return LOCAL_GL_FRAMEBUFFER_UNSUPPORTED; + } return LOCAL_GL_FRAMEBUFFER_COMPLETE; } @@ -810,12 +793,12 @@ WebGLFramebuffer::PrecheckFramebufferStatus() const FBStatus WebGLFramebuffer::CheckFramebufferStatus() const { - if (mStatus != 0) - return mStatus; + if (mIsKnownFBComplete) + return LOCAL_GL_FRAMEBUFFER_COMPLETE; - mStatus = PrecheckFramebufferStatus().get(); - if (mStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE) - return mStatus; + FBStatus ret = PrecheckFramebufferStatus(); + if (ret != LOCAL_GL_FRAMEBUFFER_COMPLETE) + return ret; // Looks good on our end. Let's ask the driver. mContext->MakeContextCurrent(); @@ -824,8 +807,12 @@ WebGLFramebuffer::CheckFramebufferStatus() const FinalizeAttachments(); // TODO: This should not be unconditionally GL_FRAMEBUFFER. - mStatus = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); - return mStatus; + ret = mContext->gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + + if (ret == LOCAL_GL_FRAMEBUFFER_COMPLETE) + mIsKnownFBComplete = true; + + return ret; } bool @@ -864,14 +851,22 @@ WebGLFramebuffer::CheckAndInitializeAttachments() return false; // Cool! We've checked out ok. Just need to initialize. - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); // Check if we need to initialize anything { bool hasUninitializedAttachments = false; - if (mColorAttachment0.HasImage()) + if (mColorAttachment0.HasImage() && IsDrawBuffer(0)) hasUninitializedAttachments |= mColorAttachment0.HasUninitializedImageData(); + + size_t i = 1; + for (const auto& cur : mMoreColorAttachments) { + if (cur.HasImage() && IsDrawBuffer(i)) + hasUninitializedAttachments |= cur.HasUninitializedImageData(); + + ++i; + } + if (mDepthAttachment.HasImage()) hasUninitializedAttachments |= mDepthAttachment.HasUninitializedImageData(); if (mStencilAttachment.HasImage()) @@ -879,50 +874,67 @@ WebGLFramebuffer::CheckAndInitializeAttachments() if (mDepthStencilAttachment.HasImage()) hasUninitializedAttachments |= mDepthStencilAttachment.HasUninitializedImageData(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].HasImage()) - hasUninitializedAttachments |= mMoreColorAttachments[i].HasUninitializedImageData(); - } - if (!hasUninitializedAttachments) return true; } // Get buffer-bit-mask and color-attachment-mask-list - uint32_t mask = 0; - bool colorAttachmentsMask[WebGLContext::kMaxColorAttachments] = { false }; - MOZ_ASSERT(1 + moreColorAttachmentCount <= WebGLContext::kMaxColorAttachments); + uint32_t clearBits = 0; + std::vector tempDrawBuffers(1 + mMoreColorAttachments.Size(), LOCAL_GL_NONE); - if (mColorAttachment0.HasUninitializedImageData()) { - colorAttachmentsMask[0] = true; - mask |= LOCAL_GL_COLOR_BUFFER_BIT; + if (mColorAttachment0.HasUninitializedImageData() && IsDrawBuffer(0)) { + clearBits |= LOCAL_GL_COLOR_BUFFER_BIT; + tempDrawBuffers[0] = LOCAL_GL_COLOR_ATTACHMENT0; + } + + size_t i = 1; + for (const auto& cur : mMoreColorAttachments) { + if (cur.HasUninitializedImageData() && IsDrawBuffer(i)) { + clearBits |= LOCAL_GL_COLOR_BUFFER_BIT; + tempDrawBuffers[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i; + } + + ++i; } if (mDepthAttachment.HasUninitializedImageData() || mDepthStencilAttachment.HasUninitializedImageData()) { - mask |= LOCAL_GL_DEPTH_BUFFER_BIT; + clearBits |= LOCAL_GL_DEPTH_BUFFER_BIT; } if (mStencilAttachment.HasUninitializedImageData() || mDepthStencilAttachment.HasUninitializedImageData()) { - mask |= LOCAL_GL_STENCIL_BUFFER_BIT; + clearBits |= LOCAL_GL_STENCIL_BUFFER_BIT; } - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].HasUninitializedImageData()) { - colorAttachmentsMask[1 + i] = true; - mask |= LOCAL_GL_COLOR_BUFFER_BIT; + mContext->MakeContextCurrent(); + + const auto fnDrawBuffers = [this](const std::vector& list) { + const GLenum* ptr = nullptr; + if (list.size()) { + ptr = &(list[0]); } + this->mContext->gl->fDrawBuffers(list.size(), ptr); + }; + + const auto drawBufferExt = WebGLExtensionID::WEBGL_draw_buffers; + const bool hasDrawBuffers = (mContext->IsWebGL2() || + mContext->IsExtensionEnabled(drawBufferExt)); + + if (hasDrawBuffers) { + fnDrawBuffers(tempDrawBuffers); } // Clear! - mContext->ForceClearFramebufferWithDefaultValues(false, mask, colorAttachmentsMask); + mContext->ForceClearFramebufferWithDefaultValues(clearBits, false); + + if (hasDrawBuffers) { + fnDrawBuffers(mDrawBuffers); + } // Mark all the uninitialized images as initialized. - if (mColorAttachment0.HasUninitializedImageData()) - mColorAttachment0.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); if (mDepthAttachment.HasUninitializedImageData()) mDepthAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); if (mStencilAttachment.HasUninitializedImageData()) @@ -930,31 +942,21 @@ WebGLFramebuffer::CheckAndInitializeAttachments() if (mDepthStencilAttachment.HasUninitializedImageData()) mDepthStencilAttachment.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - if (mMoreColorAttachments[i].HasUninitializedImageData()) - mMoreColorAttachments[i].SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); + if (mColorAttachment0.HasUninitializedImageData() && IsDrawBuffer(0)) { + mColorAttachment0.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); + } + + i = 1; + for (auto& cur : mMoreColorAttachments) { + if (cur.HasUninitializedImageData() && IsDrawBuffer(i)) + cur.SetImageDataStatus(WebGLImageDataStatus::InitializedImageData); + + ++i; } return true; } -void WebGLFramebuffer::EnsureColorAttachPoints(size_t colorAttachmentId) -{ - size_t maxColorAttachments = mContext->mGLMaxColorAttachments; - - MOZ_ASSERT(colorAttachmentId < maxColorAttachments); - - if (colorAttachmentId < ColorAttachmentCount()) - return; - - while (ColorAttachmentCount() < maxColorAttachments) { - GLenum nextAttachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + ColorAttachmentCount(); - mMoreColorAttachments.AppendElement(WebGLFBAttachPoint(this, nextAttachPoint)); - } - - MOZ_ASSERT(ColorAttachmentCount() == maxColorAttachments); -} - static void FinalizeDrawAndReadBuffers(gl::GLContext* gl, bool isColorBufferDefined) { @@ -990,8 +992,6 @@ WebGLFramebuffer::FinalizeAttachments() const MOZ_ASSERT(mContext->mBoundDrawFramebuffer == this || mContext->mBoundReadFramebuffer == this); - MOZ_ASSERT(mStatus == LOCAL_GL_FRAMEBUFFER_COMPLETE); - gl::GLContext* gl = mContext->gl; // Nuke the depth and stencil attachment points. @@ -1006,9 +1006,8 @@ WebGLFramebuffer::FinalizeAttachments() const mStencilAttachment.FinalizeAttachment(gl, LOCAL_GL_STENCIL_ATTACHMENT); mDepthStencilAttachment.FinalizeAttachment(gl, LOCAL_GL_DEPTH_STENCIL_ATTACHMENT); - const size_t moreColorAttachmentCount = mMoreColorAttachments.Length(); - for (size_t i = 0; i < moreColorAttachmentCount; i++) { - GLenum attachPoint = LOCAL_GL_COLOR_ATTACHMENT0 + 1 + i; + for (size_t i = 0; i < mMoreColorAttachments.Size(); i++) { + GLenum attachPoint = LOCAL_GL_COLOR_ATTACHMENT1 + i; mMoreColorAttachments[i].FinalizeAttachment(gl, attachPoint); } @@ -1016,34 +1015,31 @@ WebGLFramebuffer::FinalizeAttachments() const } bool -WebGLFramebuffer::ValidateForRead(const char* info, TexInternalFormat* const out_format) +WebGLFramebuffer::ValidateForRead(const char* funcName, + const webgl::FormatUsageInfo** const out_format, + uint32_t* const out_width, uint32_t* const out_height) { + if (!CheckAndInitializeAttachments()) { + mContext->ErrorInvalidFramebufferOperation("%s: Incomplete framebuffer.", + funcName); + return false; + } + if (mReadBufferMode == LOCAL_GL_NONE) { mContext->ErrorInvalidOperation("%s: Read buffer mode must not be" - " NONE.", info); + " NONE.", funcName); return false; } - const auto& attachPoint = GetAttachPoint(mReadBufferMode); - - if (!CheckAndInitializeAttachments()) { - mContext->ErrorInvalidFramebufferOperation("readPixels: incomplete framebuffer"); + const auto attachPoint = GetAttachPoint(mReadBufferMode); + if (!attachPoint || !attachPoint->IsDefined()) { + mContext->ErrorInvalidOperation("%s: The attachment specified for reading is" + " null.", funcName); return false; } - GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT; - if (!HasCompletePlanes(readPlaneBits)) { - mContext->ErrorInvalidOperation("readPixels: Read source attachment doesn't have the" - " correct color/depth/stencil type."); - return false; - } - - if (!attachPoint.IsDefined()) { - mContext->ErrorInvalidOperation("readPixels: "); - return false; - } - - *out_format = attachPoint.EffectiveInternalFormat(); + *out_format = attachPoint->Format(); + attachPoint->Size(out_width, out_height); return true; } @@ -1062,93 +1058,43 @@ AttachmentsDontMatch(const WebGLFBAttachPoint& a, const WebGLFBAttachPoint& b) } JS::Value -WebGLFramebuffer::GetAttachmentParameter(JSContext* cx, - GLenum attachment, - GLenum pname, - ErrorResult& rv) +WebGLFramebuffer::GetAttachmentParameter(const char* funcName, JSContext* cx, + GLenum target, GLenum attachment, + GLenum pname, ErrorResult* const out_error) { - // "If a framebuffer object is bound to target, then attachment must be one of the - // attachment points of the framebuffer listed in table 4.6." - switch (attachment) { - case LOCAL_GL_DEPTH_ATTACHMENT: - case LOCAL_GL_DEPTH_STENCIL_ATTACHMENT: - break; + auto attachPoint = GetAttachPoint(attachment); + if (!attachPoint) { + mContext->ErrorInvalidEnum("%s: Can only query COLOR_ATTACHMENTi," + " DEPTH_ATTACHMENT, DEPTH_STENCIL_ATTACHMENT, or" + " STENCIL_ATTACHMENT for a framebuffer.", + funcName); + return JS::NullValue(); + } - case LOCAL_GL_STENCIL_ATTACHMENT: - // "If attachment is DEPTH_STENCIL_ATTACHMENT, and different objects are bound to - // the depth and stencil attachment points of target, the query will fail and - // generate an INVALID_OPERATION error. If the same object is bound to both - // attachment points, information about that object will be returned." + if (mContext->IsWebGL2() && attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { + // There are a couple special rules for this one. + + if (pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) { + mContext->ErrorInvalidOperation("%s: Querying" + " FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE" + " against DEPTH_STENCIL_ATTACHMENT is an" + " error.", + funcName); + return JS::NullValue(); + } - // Does this mean it has to be the same level or layer? Because the queries are - // independent of level or layer. if (AttachmentsDontMatch(DepthAttachment(), StencilAttachment())) { - mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: " - "DEPTH_ATTACHMENT and STENCIL_ATTACHMENT " - "have different objects bound."); - return JS::NullValue(); - } - break; - - default: - if (attachment < LOCAL_GL_COLOR_ATTACHMENT0 || - attachment > mContext->LastColorAttachment()) - { - mContext->ErrorInvalidEnum("getFramebufferAttachmentParameter: Can only " - "query COLOR_ATTACHMENTi, DEPTH_ATTACHMENT, " - "DEPTH_STENCIL_ATTACHMENT, or STENCIL_ATTACHMENT " - "on framebuffer."); - return JS::NullValue(); - } - } - - if (attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT && - pname == LOCAL_GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE) - { - mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: Querying " - "FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE against " - "DEPTH_STENCIL_ATTACHMENT is an error."); - return JS::NullValue(); - } - - GLenum objectType = LOCAL_GL_NONE; - auto& fba = GetAttachPoint(attachment); - if (fba.Texture()) { - objectType = LOCAL_GL_TEXTURE; - } else if (fba.Renderbuffer()) { - objectType = LOCAL_GL_RENDERBUFFER; - } - - switch (pname) { - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: - return JS::Int32Value(objectType); - - case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: - if (objectType == LOCAL_GL_NONE) { + mContext->ErrorInvalidOperation("%s: DEPTH_ATTACHMENT and STENCIL_ATTACHMENT" + " have different objects bound.", + funcName); return JS::NullValue(); } - if (objectType == LOCAL_GL_RENDERBUFFER) { - const WebGLRenderbuffer* rb = fba.Renderbuffer(); - return mContext->WebGLObjectAsJSValue(cx, rb, rv); - } - - /* If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE, then */ - if (objectType == LOCAL_GL_TEXTURE) { - const WebGLTexture* tex = fba.Texture(); - return mContext->WebGLObjectAsJSValue(cx, tex, rv); - } - break; + attachPoint = GetAttachPoint(LOCAL_GL_DEPTH_ATTACHMENT); } - if (objectType == LOCAL_GL_NONE) { - mContext->ErrorInvalidOperation("getFramebufferAttachmentParameter: No " - "attachment at %s", - mContext->EnumName(attachment)); - return JS::NullValue(); - } - - return fba.GetParameter(mContext, pname); + return attachPoint->GetParameter(funcName, mContext, cx, target, attachment, pname, + out_error); } //////////////////////////////////////////////////////////////////////////////// @@ -1176,6 +1122,27 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, CycleCollectionNoteChild(callback, field.Renderbuffer(), name, flags); } +template +inline void +ImplCycleCollectionUnlink(C& field) +{ + for (auto& cur : field) { + cur.Unlink(); + } +} + +template +inline void +ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback, + C& field, + const char* name, + uint32_t flags = 0) +{ + for (auto& cur : field) { + ImplCycleCollectionTraverse(callback, cur, name, flags); + } +} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLFramebuffer, mColorAttachment0, mDepthAttachment, diff --git a/dom/canvas/WebGLFramebuffer.h b/dom/canvas/WebGLFramebuffer.h index a832c1ad2c..82e73b32a8 100644 --- a/dom/canvas/WebGLFramebuffer.h +++ b/dom/canvas/WebGLFramebuffer.h @@ -21,6 +21,9 @@ class WebGLFramebuffer; class WebGLRenderbuffer; class WebGLTexture; +template +class PlacementArray; + namespace gl { class GLContext; } // namespace gl @@ -29,34 +32,39 @@ class WebGLFBAttachPoint { public: WebGLFramebuffer* const mFB; + const GLenum mAttachmentPoint; private: WebGLRefPtr mTexturePtr; WebGLRefPtr mRenderbufferPtr; - FBAttachment mAttachmentPoint; TexImageTarget mTexImageTarget; GLint mTexImageLayer; GLint mTexImageLevel; + // PlacementArray needs a default constructor. + template + friend class PlacementArray; + + WebGLFBAttachPoint() + : mFB(nullptr) + , mAttachmentPoint(0) + { } + + public: - WebGLFBAttachPoint(WebGLFramebuffer* fb, FBAttachment attachmentPoint); + WebGLFBAttachPoint(WebGLFramebuffer* fb, GLenum attachmentPoint); ~WebGLFBAttachPoint(); - void Unlink() { - mRenderbufferPtr = nullptr; - mTexturePtr = nullptr; - } + void Unlink(); bool IsDefined() const; bool IsDeleteRequested() const; - TexInternalFormat EffectiveInternalFormat() const; + const webgl::FormatUsageInfo* Format() const; bool HasAlpha() const; bool IsReadableFloat() const; - void Clear() { - SetRenderbuffer(nullptr); - } + void Clear(); void SetTexImage(WebGLTexture* tex, TexImageTarget target, GLint level); void SetTexImageLayer(WebGLTexture* tex, TexImageTarget target, GLint level, @@ -88,15 +96,68 @@ public: bool HasUninitializedImageData() const; void SetImageDataStatus(WebGLImageDataStatus x); - const WebGLRectangleObject& RectangleObject() const; + void Size(uint32_t* const out_width, uint32_t* const out_height) const; + //const WebGLRectangleObject& RectangleObject() const; bool HasImage() const; - bool IsComplete() const; + bool IsComplete(WebGLContext* webgl) const; - void FinalizeAttachment(gl::GLContext* gl, - FBAttachment attachmentLoc) const; + void FinalizeAttachment(gl::GLContext* gl, GLenum attachmentLoc) const; - JS::Value GetParameter(WebGLContext* context, GLenum pname); + JS::Value GetParameter(const char* funcName, WebGLContext* webgl, JSContext* cx, + GLenum target, GLenum attachment, GLenum pname, + ErrorResult* const out_error); + + void OnBackingStoreRespecified() const; +}; + +template +class PlacementArray +{ +public: + const size_t mCapacity; +protected: + size_t mSize; + T* const mArray; + +public: + explicit PlacementArray(size_t capacity) + : mCapacity(capacity) + , mSize(0) + , mArray((T*)moz_xmalloc(sizeof(T) * capacity)) + { } + + ~PlacementArray() { + for (auto& cur : *this) { + cur.~T(); + } + free(mArray); + } + + T* begin() const { + return mArray; + } + + T* end() const { + return mArray + mSize; + } + + T& operator [](size_t offset) const { + MOZ_ASSERT(offset < mSize); + return mArray[offset]; + } + + const size_t& Size() const { return mSize; } + + template + void AppendNew(A a, B b) { + if (mSize == mCapacity) + MOZ_CRASH("Bad EmplaceAppend."); + + // Placement `new`: + new (&(mArray[mSize])) T(a, b); + ++mSize; + } }; class WebGLFramebuffer final @@ -114,7 +175,7 @@ public: const GLuint mGLName; private: - mutable GLenum mStatus; + mutable bool mIsKnownFBComplete; GLenum mReadBufferMode; @@ -123,7 +184,17 @@ private: WebGLFBAttachPoint mDepthAttachment; WebGLFBAttachPoint mStencilAttachment; WebGLFBAttachPoint mDepthStencilAttachment; - nsTArray mMoreColorAttachments; + + PlacementArray mMoreColorAttachments; + + std::vector mDrawBuffers; + + bool IsDrawBuffer(size_t n) const { + if (n < mDrawBuffers.size()) + return bool(mDrawBuffers[n]); + + return false; + } #ifdef ANDROID // Bug 1140459: Some drivers (including our test slaves!) don't @@ -147,14 +218,11 @@ private: public: void Delete(); - void FramebufferRenderbuffer(FBAttachment attachment, RBTarget rbtarget, + void FramebufferRenderbuffer(GLenum attachment, RBTarget rbtarget, WebGLRenderbuffer* rb); - - void FramebufferTexture2D(FBAttachment attachment, - TexImageTarget texImageTarget, WebGLTexture* tex, - GLint level); - - void FramebufferTextureLayer(FBAttachment attachment, WebGLTexture* tex, GLint level, + void FramebufferTexture2D(GLenum attachment, TexImageTarget texImageTarget, + WebGLTexture* tex, GLint level); + void FramebufferTextureLayer(GLenum attachment, WebGLTexture* tex, GLint level, GLint layer); bool HasDefinedAttachments() const; @@ -163,20 +231,11 @@ public: FBStatus PrecheckFramebufferStatus() const; FBStatus CheckFramebufferStatus() const; - GLenum + const webgl::FormatUsageInfo* GetFormatForAttachment(const WebGLFBAttachPoint& attachment) const; - bool HasDepthStencilConflict() const { - return int(mDepthAttachment.IsDefined()) + - int(mStencilAttachment.IsDefined()) + - int(mDepthStencilAttachment.IsDefined()) >= 2; - } - - size_t ColorAttachmentCount() const { - return 1 + mMoreColorAttachments.Length(); - } const WebGLFBAttachPoint& ColorAttachment(size_t colorAttachmentId) const { - MOZ_ASSERT(colorAttachmentId < ColorAttachmentCount()); + MOZ_ASSERT(colorAttachmentId < 1 + mMoreColorAttachments.Size()); return colorAttachmentId ? mMoreColorAttachments[colorAttachmentId - 1] : mColorAttachment0; } @@ -193,16 +252,16 @@ public: return mDepthStencilAttachment; } - WebGLFBAttachPoint& GetAttachPoint(FBAttachment attachPointEnum); +protected: + WebGLFBAttachPoint* GetAttachPoint(GLenum attachment); // Fallible +public: void DetachTexture(const WebGLTexture* tex); void DetachRenderbuffer(const WebGLRenderbuffer* rb); - const WebGLRectangleObject& RectangleObject() const; - WebGLContext* GetParentObject() const { - return Context(); + return mContext; } void FinalizeAttachments() const; @@ -217,19 +276,17 @@ public: bool CheckAndInitializeAttachments(); - bool CheckColorAttachmentNumber(FBAttachment attachment, - const char* funcName) const; - - void EnsureColorAttachPoints(size_t colorAttachmentId); - void InvalidateFramebufferStatus() const { - mStatus = 0; + mIsKnownFBComplete = false; } - bool ValidateForRead(const char* info, TexInternalFormat* const out_format); + bool ValidateForRead(const char* info, + const webgl::FormatUsageInfo** const out_format, + uint32_t* const out_width, uint32_t* const out_height); - JS::Value GetAttachmentParameter(JSContext* cx, GLenum attachment, GLenum pname, - ErrorResult& rv); + JS::Value GetAttachmentParameter(const char* funcName, JSContext* cx, GLenum target, + GLenum attachment, GLenum pname, + ErrorResult* const out_error); }; } // namespace mozilla diff --git a/dom/canvas/WebGLObjectModel.h b/dom/canvas/WebGLObjectModel.h index fa489d7bc2..9cf6de244e 100644 --- a/dom/canvas/WebGLObjectModel.h +++ b/dom/canvas/WebGLObjectModel.h @@ -269,10 +269,8 @@ public: bool IsCompatibleWithContext(WebGLContext* other); - WebGLContext* Context() const { return mContext; } - -protected: WebGLContext* const mContext; +protected: const uint32_t mContextGeneration; }; diff --git a/dom/canvas/WebGLProgram.cpp b/dom/canvas/WebGLProgram.cpp index 705e9076e2..83b9e875bc 100644 --- a/dom/canvas/WebGLProgram.cpp +++ b/dom/canvas/WebGLProgram.cpp @@ -167,7 +167,7 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) #endif const bool isArray = false; - AddActiveInfo(prog->Context(), elemCount, elemType, isArray, userName, mappedName, + AddActiveInfo(prog->mContext, elemCount, elemType, isArray, userName, mappedName, &info->activeAttribs, &info->attribMap); // Collect active locations: @@ -232,7 +232,7 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) printf_stderr(" isArray: %d\n", (int)isArray); #endif - AddActiveInfo(prog->Context(), elemCount, elemType, isArray, baseUserName, + AddActiveInfo(prog->mContext, elemCount, elemType, isArray, baseUserName, baseMappedName, &info->activeUniforms, &info->uniformMap); } @@ -327,8 +327,9 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) } } - AddActiveInfo(prog->Context(), size, type, isArray, baseUserName, mappedName, - &info->transformFeedbackVaryings, &info->transformFeedbackVaryingsMap); + AddActiveInfo(prog->mContext, size, type, isArray, baseUserName, mappedName, + &info->transformFeedbackVaryings, + &info->transformFeedbackVaryingsMap); } } @@ -337,7 +338,6 @@ QueryProgramInfo(WebGLProgram* prog, gl::GLContext* gl) //////////////////////////////////////////////////////////////////////////////// - webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog) : prog(prog) , fragDataMap(nullptr) diff --git a/dom/canvas/WebGLProgram.h b/dom/canvas/WebGLProgram.h index 29faebaca6..44ca036235 100644 --- a/dom/canvas/WebGLProgram.h +++ b/dom/canvas/WebGLProgram.h @@ -194,7 +194,7 @@ public: } WebGLContext* GetParentObject() const { - return Context(); + return mContext; } virtual JSObject* WrapObject(JSContext* js, JS::Handle givenProto) override; diff --git a/dom/canvas/WebGLQuery.h b/dom/canvas/WebGLQuery.h index 46cd561c3e..68a579786f 100644 --- a/dom/canvas/WebGLQuery.h +++ b/dom/canvas/WebGLQuery.h @@ -33,7 +33,7 @@ public: // nsWrapperCache WebGLContext* GetParentObject() const { - return Context(); + return mContext; } // NS diff --git a/dom/canvas/WebGLRenderbuffer.cpp b/dom/canvas/WebGLRenderbuffer.cpp index 05b6023a6f..a555d44984 100644 --- a/dom/canvas/WebGLRenderbuffer.cpp +++ b/dom/canvas/WebGLRenderbuffer.cpp @@ -51,10 +51,10 @@ WebGLRenderbuffer::WebGLRenderbuffer(WebGLContext* webgl) : WebGLContextBoundObject(webgl) , mPrimaryRB(0) , mSecondaryRB(0) - , mInternalFormat(0) - , mInternalFormatForGL(0) + , mFormat(nullptr) , mImageDataStatus(WebGLImageDataStatus::NoImageData) , mSamples(1) + , mIsUsingSecondary(false) #ifdef ANDROID , mIsRB(false) #endif @@ -86,62 +86,21 @@ WebGLRenderbuffer::Delete() int64_t WebGLRenderbuffer::MemoryUsage() const { - int64_t pixels = int64_t(Width()) * int64_t(Height()); - - GLenum primaryFormat = InternalFormatForGL(); // If there is no defined format, we're not taking up any memory - if (!primaryFormat) + if (!mFormat) return 0; - int64_t secondarySize = 0; - if (mSecondaryRB) { - if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) { - primaryFormat = DepthStencilDepthFormat(mContext->gl); - secondarySize = 1*pixels; // STENCIL_INDEX8 - } else { - secondarySize = 1*1*2; // 1x1xRGBA4 - } + auto bytesPerPixel = mFormat->format->estimatedBytesPerPixel; + uint64_t pixels = uint64_t(mWidth) * uint64_t(mHeight); + + uint64_t totalSize = pixels * bytesPerPixel; + + // If we have the same bytesPerPixel whether or not we have a secondary RB. + if (mSecondaryRB && !mIsUsingSecondary) { + totalSize += 2; // 1x1xRGBA4 } - int64_t primarySize = 0; - switch (primaryFormat) { - case LOCAL_GL_STENCIL_INDEX8: - primarySize = 1*pixels; - break; - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGB5_A1: - case LOCAL_GL_RGB565: - case LOCAL_GL_DEPTH_COMPONENT16: - primarySize = 2*pixels; - break; - case LOCAL_GL_RGB8: - case LOCAL_GL_DEPTH_COMPONENT24: - primarySize = 3*pixels; - break; - case LOCAL_GL_RGBA8: - case LOCAL_GL_SRGB8_ALPHA8_EXT: - case LOCAL_GL_DEPTH24_STENCIL8: - case LOCAL_GL_DEPTH_COMPONENT32: - primarySize = 4*pixels; - break; - case LOCAL_GL_RGB16F: - primarySize = 2*3*pixels; - break; - case LOCAL_GL_RGBA16F: - primarySize = 2*4*pixels; - break; - case LOCAL_GL_RGB32F: - primarySize = 4*3*pixels; - break; - case LOCAL_GL_RGBA32F: - primarySize = 4*4*pixels; - break; - default: - MOZ_ASSERT(false, "Unknown `primaryFormat`."); - break; - } - - return primarySize + secondarySize; + return int64_t(totalSize); } void @@ -172,27 +131,60 @@ RenderbufferStorageMaybeMultisample(gl::GLContext* gl, GLsizei samples, MOZ_ASSERT(samples >= 0); MOZ_ASSERT(samples <= gl->MaxSamples()); + // certain OpenGL ES renderbuffer formats may not exist on desktop OpenGL + GLenum internalFormatForGL = internalFormat; + + switch (internalFormat) { + case LOCAL_GL_RGBA4: + case LOCAL_GL_RGB5_A1: + // 16-bit RGBA formats are not supported on desktop GL + if (!gl->IsGLES()) + internalFormatForGL = LOCAL_GL_RGBA8; + break; + + case LOCAL_GL_RGB565: + // the RGB565 format is not supported on desktop GL + if (!gl->IsGLES()) + internalFormatForGL = LOCAL_GL_RGB8; + break; + + case LOCAL_GL_DEPTH_COMPONENT16: + if (!gl->IsGLES() || gl->IsExtensionSupported(gl::GLContext::OES_depth24)) + internalFormatForGL = LOCAL_GL_DEPTH_COMPONENT24; + else if (gl->IsExtensionSupported(gl::GLContext::OES_packed_depth_stencil)) + internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8; + break; + + case LOCAL_GL_DEPTH_STENCIL: + // We emulate this in WebGLRenderbuffer if we don't have the requisite extension. + internalFormatForGL = LOCAL_GL_DEPTH24_STENCIL8; + break; + + default: + break; + } + if (samples > 0) { gl->fRenderbufferStorageMultisample(LOCAL_GL_RENDERBUFFER, samples, - internalFormat, width, height); + internalFormatForGL, width, height); } else { - gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormat, width, + gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, internalFormatForGL, width, height); } } void -WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, GLenum internalFormat, - GLsizei width, GLsizei height) const +WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, + const webgl::FormatUsageInfo* format, + GLsizei width, GLsizei height) { MOZ_ASSERT(mContext->mBoundRenderbuffer == this); - InvalidateStatusOfAttachedFBs(); gl::GLContext* gl = mContext->gl; MOZ_ASSERT(samples >= 0 && samples <= 256); // Sanity check. - GLenum primaryFormat = internalFormat; + GLenum primaryFormat = format->format->sizedFormat; GLenum secondaryFormat = 0; if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) { @@ -203,35 +195,42 @@ WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, GLenum internalFormat, RenderbufferStorageMaybeMultisample(gl, samples, primaryFormat, width, height); - if (!mSecondaryRB) { - MOZ_ASSERT(!secondaryFormat); - return; - } - // We can't leave the secondary RB unspecified either, since we should - // handle the case where we attach a non-depth-stencil RB to a - // depth-stencil attachment point, or attach this depth-stencil RB to a - // non-depth-stencil attachment point. - gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB); - if (secondaryFormat) { - RenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, width, - height); - } else { - RenderbufferStorageMaybeMultisample(gl, samples, LOCAL_GL_RGBA4, 1, 1); + if (mSecondaryRB) { + // We can't leave the secondary RB unspecified either, since we should + // handle the case where we attach a non-depth-stencil RB to a + // depth-stencil attachment point, or attach this depth-stencil RB to a + // non-depth-stencil attachment point. + gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB); + if (secondaryFormat) { + RenderbufferStorageMaybeMultisample(gl, samples, secondaryFormat, width, + height); + } else { + RenderbufferStorageMaybeMultisample(gl, samples, LOCAL_GL_RGBA4, 1, 1); + } } + + mSamples = samples; + mFormat = format; + mWidth = width; + mHeight = height; + mImageDataStatus = WebGLImageDataStatus::UninitializedImageData; + mIsUsingSecondary = bool(secondaryFormat); + + InvalidateStatusOfAttachedFBs(); } void -WebGLRenderbuffer::FramebufferRenderbuffer(FBAttachment attachment) const +WebGLRenderbuffer::FramebufferRenderbuffer(GLenum attachment) const { gl::GLContext* gl = mContext->gl; if (attachment != LOCAL_GL_DEPTH_STENCIL_ATTACHMENT) { - gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment.get(), + gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, attachment, LOCAL_GL_RENDERBUFFER, mPrimaryRB); return; } GLuint stencilRB = mPrimaryRB; - if (NeedsDepthStencilEmu(mContext->gl, InternalFormatForGL())) { + if (mIsUsingSecondary) { MOZ_ASSERT(mSecondaryRB); stencilRB = mSecondaryRB; } @@ -251,20 +250,13 @@ WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target, switch (pname.get()) { case LOCAL_GL_RENDERBUFFER_STENCIL_SIZE: - if (NeedsDepthStencilEmu(mContext->gl, InternalFormatForGL())) { - if (gl->WorkAroundDriverBugs() && - gl->Renderer() == gl::GLRenderer::Tegra) - { - return 8; - } + if (!mFormat) + return 0; - gl::ScopedBindRenderbuffer autoRB(gl, mSecondaryRB); + if (!mFormat->format->hasStencil) + return 0; - GLint i = 0; - gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i); - return i; - } - // Fall through otherwise. + return 8; case LOCAL_GL_RENDERBUFFER_WIDTH: case LOCAL_GL_RENDERBUFFER_HEIGHT: @@ -278,6 +270,19 @@ WebGLRenderbuffer::GetRenderbufferParameter(RBTarget target, gl->fGetRenderbufferParameteriv(target.get(), pname.get(), &i); return i; } + + case LOCAL_GL_RENDERBUFFER_INTERNAL_FORMAT: + { + GLenum ret = 0; + if (mFormat) { + ret = mFormat->format->sizedFormat; + + if (!mContext->IsWebGL2() && ret == LOCAL_GL_DEPTH24_STENCIL8) { + ret = LOCAL_GL_DEPTH_STENCIL; + } + } + return ret; + } } MOZ_ASSERT(false, diff --git a/dom/canvas/WebGLRenderbuffer.h b/dom/canvas/WebGLRenderbuffer.h index 40d235faa2..d53e80016e 100644 --- a/dom/canvas/WebGLRenderbuffer.h +++ b/dom/canvas/WebGLRenderbuffer.h @@ -14,6 +14,9 @@ #include "WebGLStrongTypes.h" namespace mozilla { +namespace webgl { +struct FormatUsageInfo; +} class WebGLRenderbuffer final : public nsWrapperCache @@ -29,40 +32,34 @@ public: void Delete(); bool HasUninitializedImageData() const { + MOZ_ASSERT(mImageDataStatus != WebGLImageDataStatus::NoImageData); return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData; } - void SetImageDataStatus(WebGLImageDataStatus x) { - // there is no way to go from having image data to not having any - MOZ_ASSERT(x != WebGLImageDataStatus::NoImageData || - mImageDataStatus == WebGLImageDataStatus::NoImageData); - mImageDataStatus = x; + + bool IsDefined() const { + if (!mFormat) { + MOZ_ASSERT(!mWidth && !mHeight); + return false; + } + return true; } GLsizei Samples() const { return mSamples; } - void SetSamples(GLsizei samples) { mSamples = samples; } GLuint PrimaryGLName() const { return mPrimaryRB; } - GLenum InternalFormat() const { return mInternalFormat; } - void SetInternalFormat(GLenum internalFormat) { - mInternalFormat = internalFormat; - } - - GLenum InternalFormatForGL() const { return mInternalFormatForGL; } - void SetInternalFormatForGL(GLenum internalFormatForGL) { - mInternalFormatForGL = internalFormatForGL; - } + const webgl::FormatUsageInfo* Format() const { return mFormat; } int64_t MemoryUsage() const; WebGLContext* GetParentObject() const { - return Context(); + return mContext; } void BindRenderbuffer() const; - void RenderbufferStorage(GLsizei samples, GLenum internalFormat, - GLsizei width, GLsizei height) const; - void FramebufferRenderbuffer(FBAttachment attachment) const; + void RenderbufferStorage(GLsizei samples, const webgl::FormatUsageInfo* format, + GLsizei width, GLsizei height); + void FramebufferRenderbuffer(GLenum attachment) const; // Only handles a subset of `pname`s. GLint GetRenderbufferParameter(RBTarget target, RBParam pname) const; @@ -78,10 +75,10 @@ protected: GLuint mPrimaryRB; GLuint mSecondaryRB; - GLenum mInternalFormat; - GLenum mInternalFormatForGL; + const webgl::FormatUsageInfo* mFormat; WebGLImageDataStatus mImageDataStatus; GLsizei mSamples; + bool mIsUsingSecondary; #ifdef ANDROID // Bug 1140459: Some drivers (including our test slaves!) don't // give reasonable answers for IsRenderbuffer, maybe others. @@ -93,6 +90,7 @@ protected: friend class WebGLContext; friend class WebGLFramebuffer; + friend class WebGLFBAttachPoint; }; } // namespace mozilla diff --git a/dom/canvas/WebGLSampler.cpp b/dom/canvas/WebGLSampler.cpp index ed2695de31..87ee1a0edb 100644 --- a/dom/canvas/WebGLSampler.cpp +++ b/dom/canvas/WebGLSampler.cpp @@ -35,7 +35,7 @@ WebGLSampler::Delete() WebGLContext* WebGLSampler::GetParentObject() const { - return Context(); + return mContext; } JSObject* diff --git a/dom/canvas/WebGLShader.cpp b/dom/canvas/WebGLShader.cpp index dc5c5023ad..8acb0f99a4 100644 --- a/dom/canvas/WebGLShader.cpp +++ b/dom/canvas/WebGLShader.cpp @@ -11,6 +11,7 @@ #include "mozilla/MemoryReporting.h" #include "nsPrintfCString.h" #include "nsString.h" +#include "prenv.h" #include "WebGLContext.h" #include "WebGLObjectModel.h" #include "WebGLShaderValidator.h" @@ -165,7 +166,7 @@ WebGLShader::ShaderSource(const nsAString& source) // We checked that the source stripped of comments is in the // 7-bit ASCII range, so we can skip the NS_IsAscii() check. - NS_LossyConvertUTF16toASCII sourceCString(cleanSource); + const NS_LossyConvertUTF16toASCII sourceCString(cleanSource); if (mContext->gl->WorkAroundDriverBugs()) { const size_t maxSourceLength = 0x3ffff; @@ -177,23 +178,24 @@ WebGLShader::ShaderSource(const nsAString& source) } } - // HACK - dump shader source - { -/* - printf_stderr("//-*- glsl -*-\n"); - // Wow - Roll Your Own For Each Lines because printf_stderr has a hard-coded internal size, so long strings are truncated. - const nsString& src = shader->Source(); + if (PR_GetEnv("MOZ_WEBGL_DUMP_SHADERS")) { + printf_stderr("////////////////////////////////////////\n"); + printf_stderr("// MOZ_WEBGL_DUMP_SHADERS:\n"); + + // Wow - Roll Your Own Foreach-Lines because printf_stderr has a hard-coded + // internal size, so long strings are truncated. + int32_t start = 0; - int32_t end = src.Find("\n", false, start, -1); + int32_t end = sourceCString.Find("\n", false, start, -1); while (end > -1) { - printf_stderr("%s\n", NS_ConvertUTF16toUTF8(nsDependentSubstring(src, start, end - start)).get()); + const nsCString line(sourceCString.BeginReading() + start, end - start); + printf_stderr("%s\n", line.BeginReading()); start = end + 1; - end = src.Find("\n", false, start, -1); + end = sourceCString.Find("\n", false, start, -1); } - printf_stderr("//\n"); -*/ + + printf_stderr("////////////////////////////////////////\n"); } - // HACK mSource = source; mCleanSource = sourceCString; diff --git a/dom/canvas/WebGLShader.h b/dom/canvas/WebGLShader.h index d8bdb1a617..46f8d745e0 100644 --- a/dom/canvas/WebGLShader.h +++ b/dom/canvas/WebGLShader.h @@ -77,7 +77,7 @@ public: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; void Delete(); - WebGLContext* GetParentObject() const { return Context(); } + WebGLContext* GetParentObject() const { return mContext; } virtual JSObject* WrapObject(JSContext* js, JS::Handle givenProto) override; diff --git a/dom/canvas/WebGLShaderValidator.cpp b/dom/canvas/WebGLShaderValidator.cpp index ea76a83b3d..88005188ea 100644 --- a/dom/canvas/WebGLShaderValidator.cpp +++ b/dom/canvas/WebGLShaderValidator.cpp @@ -118,16 +118,16 @@ WebGLContext::CreateShaderValidator(GLenum shaderType) const resources.MaxFragmentUniformVectors = mGLMaxFragmentUniformVectors; resources.MaxDrawBuffers = mGLMaxDrawBuffers; - if (IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_frag_depth)) resources.EXT_frag_depth = 1; - if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) resources.OES_standard_derivatives = 1; - if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers)) resources.EXT_draw_buffers = 1; - if (IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) + if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_shader_texture_lod)) resources.EXT_shader_texture_lod = 1; // Tell ANGLE to allow highp in frag shaders. (unless disabled) diff --git a/dom/canvas/WebGLStrongTypes.h b/dom/canvas/WebGLStrongTypes.h index 2b5b3ea33d..a98c1fed5d 100644 --- a/dom/canvas/WebGLStrongTypes.h +++ b/dom/canvas/WebGLStrongTypes.h @@ -223,6 +223,7 @@ STRONG_GLENUM_BEGIN(TexImageTarget) STRONG_GLENUM_VALUE(TEXTURE_CUBE_MAP_NEGATIVE_Y), STRONG_GLENUM_VALUE(TEXTURE_CUBE_MAP_POSITIVE_Z), STRONG_GLENUM_VALUE(TEXTURE_CUBE_MAP_NEGATIVE_Z), + STRONG_GLENUM_VALUE(TEXTURE_2D_ARRAY), STRONG_GLENUM_END(TexImageTarget) STRONG_GLENUM_BEGIN(TexTarget) @@ -230,6 +231,7 @@ STRONG_GLENUM_BEGIN(TexTarget) STRONG_GLENUM_VALUE(TEXTURE_2D), STRONG_GLENUM_VALUE(TEXTURE_3D), STRONG_GLENUM_VALUE(TEXTURE_CUBE_MAP), + STRONG_GLENUM_VALUE(TEXTURE_2D_ARRAY), STRONG_GLENUM_END(TexTarget) STRONG_GLENUM_BEGIN(TexType) @@ -398,28 +400,6 @@ STRONG_GLENUM_BEGIN(RBTarget) STRONG_GLENUM_VALUE(RENDERBUFFER), STRONG_GLENUM_END(RBTarget) -STRONG_GLENUM_BEGIN(FBAttachment) - STRONG_GLENUM_VALUE(DEPTH_STENCIL_ATTACHMENT), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT0), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT1), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT2), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT3), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT4), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT5), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT6), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT7), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT8), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT9), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT10), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT11), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT12), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT13), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT14), - STRONG_GLENUM_VALUE(COLOR_ATTACHMENT15), - STRONG_GLENUM_VALUE(DEPTH_ATTACHMENT), - STRONG_GLENUM_VALUE(STENCIL_ATTACHMENT), -STRONG_GLENUM_END(FBAttachment) - STRONG_GLENUM_BEGIN(FBStatus) STRONG_GLENUM_VALUE(FRAMEBUFFER_COMPLETE), STRONG_GLENUM_VALUE(FRAMEBUFFER_INCOMPLETE_ATTACHMENT), @@ -433,6 +413,7 @@ STRONG_GLENUM_END(FBStatus) STRONG_GLENUM_BEGIN(RBParam) STRONG_GLENUM_VALUE(RENDERBUFFER_WIDTH), STRONG_GLENUM_VALUE(RENDERBUFFER_HEIGHT), + STRONG_GLENUM_VALUE(RENDERBUFFER_INTERNAL_FORMAT), STRONG_GLENUM_VALUE(RENDERBUFFER_RED_SIZE), STRONG_GLENUM_VALUE(RENDERBUFFER_GREEN_SIZE), STRONG_GLENUM_VALUE(RENDERBUFFER_BLUE_SIZE), diff --git a/dom/canvas/WebGLSync.cpp b/dom/canvas/WebGLSync.cpp index ae021eace9..2411aa3c85 100644 --- a/dom/canvas/WebGLSync.cpp +++ b/dom/canvas/WebGLSync.cpp @@ -34,7 +34,7 @@ WebGLSync::Delete() WebGLContext* WebGLSync::GetParentObject() const { - return Context(); + return mContext; } // ------------------------------------------------------------------------- diff --git a/dom/canvas/WebGLTexelConversions.cpp b/dom/canvas/WebGLTexelConversions.cpp index 84be38c30d..61932b44f6 100644 --- a/dom/canvas/WebGLTexelConversions.cpp +++ b/dom/canvas/WebGLTexelConversions.cpp @@ -326,73 +326,73 @@ public: } // end anonymous namespace bool -WebGLContext::ConvertImage(size_t width, size_t height, size_t srcStride, size_t dstStride, - const uint8_t* src, uint8_t* dst, - WebGLTexelFormat srcFormat, bool srcPremultiplied, - WebGLTexelFormat dstFormat, bool dstPremultiplied, - size_t dstTexelSize) +ConvertImage(size_t width, size_t height, + const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin, + WebGLTexelFormat srcFormat, bool srcPremultiplied, + void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin, + WebGLTexelFormat dstFormat, bool dstPremultiplied) { - if (width <= 0 || height <= 0) - return true; - - const bool FormatsRequireNoPremultiplicationOp = - !HasAlpha(srcFormat) || - !HasColor(srcFormat) || - !HasColor(dstFormat); - - if (srcFormat == dstFormat && - (FormatsRequireNoPremultiplicationOp || srcPremultiplied == dstPremultiplied)) - { - // fast exit path: we just have to memcpy all the rows. - // - // The case where absolutely nothing needs to be done is supposed to have - // been handled earlier (in TexImage2D_base, etc). - // - // So the case we're handling here is when even though no format conversion is needed, - // we still might have to flip vertically and/or to adjust to a different stride. - - MOZ_ASSERT(mPixelStoreFlipY || srcStride != dstStride, "Performance trap -- should handle this case earlier, to avoid memcpy"); - - size_t row_size = width * dstTexelSize; // doesn't matter, src and dst formats agree - const uint8_t* ptr = src; - const uint8_t* src_end = src + height * srcStride; - - uint8_t* dst_row = mPixelStoreFlipY - ? dst + (height-1) * dstStride - : dst; - ptrdiff_t dstStrideSigned(dstStride); - ptrdiff_t dst_delta = mPixelStoreFlipY ? -dstStrideSigned : dstStrideSigned; - - while(ptr != src_end) { - memcpy(dst_row, ptr, row_size); - ptr += srcStride; - dst_row += dst_delta; - } - return true; - } - if (srcFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion || dstFormat == WebGLTexelFormat::FormatNotSupportingAnyConversion) { return false; } - uint8_t* dstStart = dst; - ptrdiff_t signedDstStride = dstStride; - if (mPixelStoreFlipY) { - dstStart = dst + (height - 1) * dstStride; - signedDstStride = -signedDstStride; + if (!width || !height) + return true; + + const bool shouldYFlip = (srcOrigin != dstOrigin); + + const bool canSkipPremult = (!HasAlpha(srcFormat) || + !HasColor(srcFormat) || + !HasColor(dstFormat)); + + WebGLTexelPremultiplicationOp premultOp; + if (canSkipPremult) { + premultOp = WebGLTexelPremultiplicationOp::None; + } else if (!srcPremultiplied && dstPremultiplied) { + premultOp = WebGLTexelPremultiplicationOp::Premultiply; + } else if (srcPremultiplied && !dstPremultiplied) { + premultOp = WebGLTexelPremultiplicationOp::Unpremultiply; + } else { + premultOp = WebGLTexelPremultiplicationOp::None; } - WebGLImageConverter converter(width, height, src, dstStart, srcStride, signedDstStride); + const uint8_t* srcItr = (const uint8_t*)srcBegin; + const uint8_t* const srcEnd = srcItr + srcStride * height; + uint8_t* dstItr = (uint8_t*)dstBegin; + ptrdiff_t dstItrStride = dstStride; + if (shouldYFlip) { + dstItr = dstItr + dstStride * (height - 1); + dstItrStride = -dstItrStride; + } - const WebGLTexelPremultiplicationOp premultiplicationOp - = FormatsRequireNoPremultiplicationOp ? WebGLTexelPremultiplicationOp::None - : (!srcPremultiplied && dstPremultiplied) ? WebGLTexelPremultiplicationOp::Premultiply - : (srcPremultiplied && !dstPremultiplied) ? WebGLTexelPremultiplicationOp::Unpremultiply - : WebGLTexelPremultiplicationOp::None; + if (srcFormat == dstFormat && premultOp == WebGLTexelPremultiplicationOp::None) { + // Fast exit path: we just have to memcpy all the rows. + // + // The case where absolutely nothing needs to be done is supposed to have + // been handled earlier (in TexImage2D_base, etc). + // + // So the case we're handling here is when even though no format conversion is + // needed, we still might have to flip vertically and/or to adjust to a different + // stride. - converter.run(srcFormat, dstFormat, premultiplicationOp); + MOZ_ASSERT(shouldYFlip || srcStride != dstStride, + "Performance trap -- should handle this case earlier to avoid memcpy"); + + const auto bytesPerPixel = TexelBytesForFormat(srcFormat); + const size_t bytesPerRow = bytesPerPixel * width; + + while (srcItr != srcEnd) { + memcpy(dstItr, srcItr, bytesPerRow); + srcItr += srcStride; + dstItr += dstItrStride; + } + return true; + } + + WebGLImageConverter converter(width, height, srcItr, dstItr, srcStride, dstItrStride); + converter.run(srcFormat, dstFormat, premultOp); if (!converter.Success()) { // the dst image may be left uninitialized, so we better not try to diff --git a/dom/canvas/WebGLTexelConversions.h b/dom/canvas/WebGLTexelConversions.h index 269d405e7a..335880f432 100644 --- a/dom/canvas/WebGLTexelConversions.h +++ b/dom/canvas/WebGLTexelConversions.h @@ -38,6 +38,14 @@ namespace mozilla { +bool ConvertImage(size_t width, size_t height, + const void* srcBegin, size_t srcStride, gl::OriginPos srcOrigin, + WebGLTexelFormat srcFormat, bool srcPremultiplied, + void* dstBegin, size_t dstStride, gl::OriginPos dstOrigin, + WebGLTexelFormat dstFormat, bool dstPremultiplied); + +////////////////////////////////////////////////////////////////////////////////////////// + // single precision float // seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm diff --git a/dom/canvas/WebGLTexture.cpp b/dom/canvas/WebGLTexture.cpp index 95cb0cf1c9..0e8f209391 100644 --- a/dom/canvas/WebGLTexture.cpp +++ b/dom/canvas/WebGLTexture.cpp @@ -11,13 +11,115 @@ #include "mozilla/gfx/Logging.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Scoped.h" +#include "mozilla/unused.h" #include "ScopedGLHelpers.h" #include "WebGLContext.h" #include "WebGLContextUtils.h" +#include "WebGLFramebuffer.h" #include "WebGLTexelConversions.h" namespace mozilla { +/*static*/ const WebGLTexture::ImageInfo WebGLTexture::ImageInfo::kUndefined; + +//////////////////////////////////////// + +template +static inline T& +Mutable(const T& x) +{ + return const_cast(x); +} + +void +WebGLTexture::ImageInfo::Clear() +{ + if (!IsDefined()) + return; + + OnRespecify(); + + Mutable(mFormat) = LOCAL_GL_NONE; + Mutable(mWidth) = 0; + Mutable(mHeight) = 0; + Mutable(mDepth) = 0; + + MOZ_ASSERT(!IsDefined()); +} + +WebGLTexture::ImageInfo& +WebGLTexture::ImageInfo::operator =(const ImageInfo& a) +{ + MOZ_ASSERT(a.IsDefined()); + + Mutable(mFormat) = a.mFormat; + Mutable(mWidth) = a.mWidth; + Mutable(mHeight) = a.mHeight; + Mutable(mDepth) = a.mDepth; + + mIsDataInitialized = a.mIsDataInitialized; + + // But *don't* transfer mAttachPoints! + MOZ_ASSERT(a.mAttachPoints.empty()); + OnRespecify(); + + return *this; +} + +bool +WebGLTexture::ImageInfo::IsPowerOfTwo() const +{ + return mozilla::IsPowerOfTwo(mWidth) && + mozilla::IsPowerOfTwo(mHeight) && + mozilla::IsPowerOfTwo(mDepth); +} + +void +WebGLTexture::ImageInfo::AddAttachPoint(WebGLFBAttachPoint* attachPoint) +{ + const auto pair = mAttachPoints.insert(attachPoint); + DebugOnly didInsert = pair.second; + MOZ_ASSERT(didInsert); +} + +void +WebGLTexture::ImageInfo::RemoveAttachPoint(WebGLFBAttachPoint* attachPoint) +{ + DebugOnly numElemsErased = mAttachPoints.erase(attachPoint); + MOZ_ASSERT_IF(IsDefined(), numElemsErased == 1); +} + +void +WebGLTexture::ImageInfo::OnRespecify() const +{ + for (auto cur : mAttachPoints) { + cur->OnBackingStoreRespecified(); + } +} + +size_t +WebGLTexture::ImageInfo::MemoryUsage() const +{ + if (!IsDefined()) + return 0; + + const auto bytesPerTexel = mFormat->format->estimatedBytesPerPixel; + return size_t(mWidth) * size_t(mHeight) * size_t(mDepth) * bytesPerTexel; +} + +void +WebGLTexture::ImageInfo::SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex) +{ + MOZ_ASSERT(tex); + MOZ_ASSERT(this >= &tex->mImageInfoArr[0]); + MOZ_ASSERT(this < &tex->mImageInfoArr[kMaxLevelCount * kMaxFaceCount]); + + mIsDataInitialized = isDataInitialized; + tex->InvalidateResolveCache(); +} + +//////////////////////////////////////// + JSObject* WebGLTexture::WrapObject(JSContext* cx, JS::Handle givenProto) { return dom::WebGLTextureBinding::Wrap(cx, this, givenProto); @@ -27,17 +129,18 @@ WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex) : WebGLContextBoundObject(webgl) , mGLName(tex) , mTarget(LOCAL_GL_NONE) + , mFaceCount(0) , mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR) , mMagFilter(LOCAL_GL_LINEAR) , mWrapS(LOCAL_GL_REPEAT) , mWrapT(LOCAL_GL_REPEAT) - , mFacesCount(0) - , mMaxLevelWithCustomImages(0) - , mHaveGeneratedMipmap(false) , mImmutable(false) + , mImmutableLevelCount(0) , mBaseMipmapLevel(0) , mMaxMipmapLevel(1000) - , mFakeBlackStatus(WebGLTextureFakeBlackStatus::IncompleteTexture) + , mTexCompareMode(LOCAL_GL_NONE) + , mIsResolved(false) + , mResolved_Swizzle(nullptr) { mContext->mTextures.insertBack(this); } @@ -45,22 +148,16 @@ WebGLTexture::WebGLTexture(WebGLContext* webgl, GLuint tex) void WebGLTexture::Delete() { - mImageInfos.Clear(); + for (auto& cur : mImageInfoArr) { + cur.Clear(); + } + mContext->MakeContextCurrent(); mContext->gl->fDeleteTextures(1, &mGLName); + LinkedListElement::removeFrom(mContext->mTextures); } -size_t -WebGLTexture::ImageInfo::MemoryUsage() const -{ - if (mImageDataStatus == WebGLImageDataStatus::NoImageData) - return 0; - - size_t bitsPerTexel = GetBitsPerTexel(mEffectiveInternalFormat); - return size_t(mWidth) * size_t(mHeight) * size_t(mDepth) * bitsPerTexel / 8; -} - size_t WebGLTexture::MemoryUsage() const { @@ -68,665 +165,489 @@ WebGLTexture::MemoryUsage() const return 0; size_t result = 0; - for(size_t face = 0; face < mFacesCount; face++) { - for(size_t level = 0; level <= mMaxLevelWithCustomImages; level++) { - result += ImageInfoAtFace(face, level).MemoryUsage(); - } - } + MOZ_CRASH("todo"); return result; } -static inline size_t -MipmapLevelsForSize(const WebGLTexture::ImageInfo& info) +void +WebGLTexture::SetImageInfo(ImageInfo* target, const ImageInfo& newInfo) { - GLsizei size = std::max(std::max(info.Width(), info.Height()), info.Depth()); + *target = newInfo; - // Find floor(log2(size)). (ES 3.0.4, 3.8 - Mipmapping). - return mozilla::FloorLog2(size); -} - -bool -WebGLTexture::DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const -{ - // We could not have generated a mipmap if the base image wasn't defined. - if (mHaveGeneratedMipmap) - return true; - - if (!IsMipmapRangeValid()) - return false; - - // We want a copy here so we can modify it temporarily. - ImageInfo expected = ImageInfoAt(texImageTarget, - EffectiveBaseMipmapLevel()); - if (!expected.IsPositive()) - return false; - - // If Level{max} is > mMaxLevelWithCustomImages, then check if we are - // missing any image levels. - if (mMaxMipmapLevel > mMaxLevelWithCustomImages) { - if (MipmapLevelsForSize(expected) > mMaxLevelWithCustomImages) - return false; - } - - // Checks if custom images are all defined up to the highest level and - // have the expected dimensions. - for (size_t level = EffectiveBaseMipmapLevel(); - level <= EffectiveMaxMipmapLevel(); ++level) - { - const ImageInfo& actual = ImageInfoAt(texImageTarget, level); - if (actual != expected) - return false; - - expected.mWidth = std::max(1, expected.mWidth / 2); - expected.mHeight = std::max(1, expected.mHeight / 2); - expected.mDepth = std::max(1, expected.mDepth / 2); - - // If the current level has size 1x1, we can stop here: The spec doesn't - // seem to forbid the existence of extra useless levels. - if (actual.mWidth == 1 && - actual.mHeight == 1 && - actual.mDepth == 1) - { - return true; - } - } - - return true; + InvalidateResolveCache(); } void -WebGLTexture::Bind(TexTarget texTarget) +WebGLTexture::SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo) { - // This function should only be called by bindTexture(). It assumes that the - // GL context is already current. - - bool firstTimeThisTextureIsBound = !HasEverBeenBound(); - - if (firstTimeThisTextureIsBound) { - mTarget = texTarget.get(); - } else if (texTarget != Target()) { - mContext->ErrorInvalidOperation("bindTexture: This texture has already" - " been bound to a different target."); - // Very important to return here before modifying texture state! This - // was the place when I lost a whole day figuring very strange "invalid - // write" crashes. - return; + for (uint8_t i = 0; i < mFaceCount; i++) { + ImageInfoAtFace(i, level) = newInfo; } - mContext->gl->fBindTexture(texTarget.get(), mGLName); - - if (firstTimeThisTextureIsBound) { - mFacesCount = (texTarget == LOCAL_GL_TEXTURE_CUBE_MAP) ? 6 : 1; - EnsureMaxLevelWithCustomImagesAtLeast(0); - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - - // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R - // is not present in GLES 2, but is present in GL and it seems as if for - // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected - // GLES behavior. - if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES()) { - mContext->gl->fTexParameteri(texTarget.get(), - LOCAL_GL_TEXTURE_WRAP_R, - LOCAL_GL_CLAMP_TO_EDGE); - } - } -} - -void -WebGLTexture::SetImageInfo(TexImageTarget texImageTarget, GLint level, - GLsizei width, GLsizei height, GLsizei depth, - TexInternalFormat effectiveInternalFormat, - WebGLImageDataStatus status) -{ - MOZ_ASSERT(depth == 1 || texImageTarget == LOCAL_GL_TEXTURE_3D); - MOZ_ASSERT(TexImageTargetToTexTarget(texImageTarget) == mTarget); - - InvalidateStatusOfAttachedFBs(); - - EnsureMaxLevelWithCustomImagesAtLeast(level); - - ImageInfoAt(texImageTarget, level) = ImageInfo(width, height, depth, - effectiveInternalFormat, - status); - - if (level > 0) - SetCustomMipmap(); - - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); -} - -void -WebGLTexture::SetGeneratedMipmap() -{ - if (!mHaveGeneratedMipmap) { - mHaveGeneratedMipmap = true; - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - } -} - -void -WebGLTexture::SetCustomMipmap() -{ - if (mHaveGeneratedMipmap) { - if (!IsMipmapRangeValid()) - return; - - // If we were in GeneratedMipmap mode and are now switching to - // CustomMipmap mode, we now need to compute all the mipmap image info. - ImageInfo imageInfo = ImageInfoAtFace(0, EffectiveBaseMipmapLevel()); - MOZ_ASSERT(mContext->IsWebGL2() || imageInfo.IsPowerOfTwo(), - "This texture is NPOT, so how could GenerateMipmap() ever" - " accept it?"); - - size_t maxRelativeLevel = MipmapLevelsForSize(imageInfo); - size_t maxLevel = EffectiveBaseMipmapLevel() + maxRelativeLevel; - EnsureMaxLevelWithCustomImagesAtLeast(maxLevel); - - for (size_t level = EffectiveBaseMipmapLevel() + 1; - level <= EffectiveMaxMipmapLevel(); ++level) - { - imageInfo.mWidth = std::max(imageInfo.mWidth / 2, 1); - imageInfo.mHeight = std::max(imageInfo.mHeight / 2, 1); - imageInfo.mDepth = std::max(imageInfo.mDepth / 2, 1); - for (size_t face = 0; face < mFacesCount; ++face) { - ImageInfoAtFace(face, level) = imageInfo; - } - } - } - mHaveGeneratedMipmap = false; -} - -bool -WebGLTexture::AreAllLevel0ImageInfosEqual() const -{ - for (size_t face = 1; face < mFacesCount; ++face) { - if (ImageInfoAtFace(face, 0) != ImageInfoAtFace(0, 0)) - return false; - } - return true; + InvalidateResolveCache(); } bool WebGLTexture::IsMipmapComplete() const { - MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_2D || - mTarget == LOCAL_GL_TEXTURE_3D); - return DoesMipmapHaveAllLevelsConsistentlyDefined(LOCAL_GL_TEXTURE_2D); + MOZ_ASSERT(DoesMinFilterRequireMipmap()); + // GLES 3.0.4, p161 + + const uint32_t maxLevel = MaxEffectiveMipmapLevel(); + + // "* `level_base <= level_max`" + if (mBaseMipmapLevel > maxLevel) + return false; + + // Make a copy so we can modify it. + const ImageInfo& baseImageInfo = BaseImageInfo(); + if (!baseImageInfo.IsDefined()) + return false; + + // Reference dimensions based on the current level. + uint32_t refWidth = baseImageInfo.mWidth; + uint32_t refHeight = baseImageInfo.mHeight; + uint32_t refDepth = baseImageInfo.mDepth; + MOZ_ASSERT(refWidth && refHeight && refDepth); + + for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) { + // "A cube map texture is mipmap complete if each of the six texture images, + // considered individually, is mipmap complete." + + for (uint8_t face = 0; face < mFaceCount; face++) { + const ImageInfo& cur = ImageInfoAtFace(face, level); + + // "* The set of mipmap arrays `level_base` through `q` (where `q` is defined + // the "Mipmapping" discussion of section 3.8.10) were each specified with + // the same effective internal format." + + // "* The dimensions of the arrays follow the sequence described in the + // "Mipmapping" discussion of section 3.8.10." + + if (cur.mWidth != refWidth || + cur.mHeight != refHeight || + cur.mDepth != refDepth || + cur.mFormat != baseImageInfo.mFormat) + { + return false; + } + } + + // GLES 3.0.4, p158: + // "[...] until the last array is reached with dimension 1 x 1 x 1." + if (refWidth == 1 && + refHeight == 1 && + refDepth == 1) + { + break; + } + + refWidth = std::max(uint32_t(1), refWidth / 2); + refHeight = std::max(uint32_t(1), refHeight / 2); + refDepth = std::max(uint32_t(1), refDepth / 2); + } + + return true; } bool WebGLTexture::IsCubeComplete() const { - MOZ_ASSERT(mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); + // GLES 3.0.4, p161 + // "[...] a cube map texture is cube complete if the following conditions all hold + // true: + // * The `level_base` arrays of each of the six texture images making up the cube map + // have identical, positive, and square dimensions. + // * The `level_base` arrays were each specified with the same effective internal + // format." - const ImageInfo& first = ImageInfoAt(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X, - 0); - if (!first.IsPositive() || !first.IsSquare()) + // Note that "cube complete" does not imply "mipmap complete". + + const ImageInfo& reference = BaseImageInfo(); + if (!reference.IsDefined()) return false; - return AreAllLevel0ImageInfosEqual(); -} + auto refWidth = reference.mWidth; + auto refFormat = reference.mFormat; -bool -WebGLTexture::IsMipmapCubeComplete() const -{ - // In particular, this checks that this is a cube map: - if (!IsCubeComplete()) - return false; - - for (int i = 0; i < 6; i++) { - const TexImageTarget face = - TexImageTargetForTargetAndFace(LOCAL_GL_TEXTURE_CUBE_MAP, i); - if (!DoesMipmapHaveAllLevelsConsistentlyDefined(face)) + for (uint8_t face = 0; face < mFaceCount; face++) { + const ImageInfo& cur = ImageInfoAtFace(face, mBaseMipmapLevel); + if (!cur.IsDefined()) return false; + + MOZ_ASSERT(cur.mDepth == 1); + if (cur.mFormat != refFormat || // Check effective formats. + cur.mWidth != refWidth || // Check both width and height against refWidth to + cur.mHeight != refWidth) // to enforce positive and square dimensions. + { + return false; + } } + return true; } bool -WebGLTexture::IsMipmapRangeValid() const +WebGLTexture::IsComplete(const char** const out_reason) const { - // In ES3, if a texture is immutable, the mipmap levels are clamped. - if (IsImmutable()) - return true; - if (mBaseMipmapLevel > std::min(mMaxLevelWithCustomImages, mMaxMipmapLevel)) + // Texture completeness is established at GLES 3.0.4, p160-161. + // "[A] texture is complete unless any of the following conditions hold true:" + + // "* Any dimension of the `level_base` array is not positive." + const ImageInfo& baseImageInfo = BaseImageInfo(); + if (!baseImageInfo.IsDefined()) { + // In case of undefined texture image, we don't print any message because this is + // a very common and often legitimate case (asynchronous texture loading). + *out_reason = nullptr; return false; + } + + if (!baseImageInfo.mWidth || !baseImageInfo.mHeight || !baseImageInfo.mDepth) { + *out_reason = "The dimensions of `level_base` are not all positive."; + return false; + } + + // "* The texture is a cube map texture, and is not cube complete." + if (IsCubeMap() && !IsCubeComplete()) { + *out_reason = "Cubemaps must be \"cube complete\"."; + return false; + } + + // "* The minification filter requires a mipmap (is neither NEAREST nor LINEAR) and + // the texture is not mipmap complete." + const bool requiresMipmap = (mMinFilter != LOCAL_GL_NEAREST && + mMinFilter != LOCAL_GL_LINEAR); + if (requiresMipmap && !IsMipmapComplete()) { + *out_reason = "Because the minification filter requires mipmapping, the texture" + " must be \"mipmap complete\"."; + return false; + } + + const bool isMinFilteringNearest = (mMinFilter == LOCAL_GL_NEAREST || + mMinFilter == LOCAL_GL_NEAREST_MIPMAP_NEAREST); + const bool isMagFilteringNearest = (mMagFilter == LOCAL_GL_NEAREST); + const bool isFilteringNearestOnly = (isMinFilteringNearest && isMagFilteringNearest); + if (!isFilteringNearestOnly) { + auto formatUsage = baseImageInfo.mFormat; + auto format = formatUsage->format; + + // "* The effective internal format specified for the texture arrays is a sized + // internal color format that is not texture-filterable, and either the + // magnification filter is not NEAREST or the minification filter is neither + // NEAREST nor NEAREST_MIPMAP_NEAREST." + // Since all (GLES3) unsized color formats are filterable just like their sized + // equivalents, we don't have to care whether its sized or not. + if (format->isColorFormat && !formatUsage->isFilterable) { + *out_reason = "Because minification or magnification filtering is not NEAREST" + " or NEAREST_MIPMAP_NEAREST, and the texture's format is a" + " color format, its format must be \"texture-filterable\"."; + return false; + } + + // "* The effective internal format specified for the texture arrays is a sized + // internal depth or depth and stencil format, the value of + // TEXTURE_COMPARE_MODE is NONE[1], and either the magnification filter is not + // NEAREST, or the minification filter is neither NEAREST nor + // NEAREST_MIPMAP_NEAREST." + // [1]: This sounds suspect, but is explicitly noted in the change log for GLES + // 3.0.1: + // "* Clarify that a texture is incomplete if it has a depth component, no + // shadow comparison, and linear filtering (also Bug 9481)." + // As of OES_packed_depth_stencil rev #3, the sample code explicitly samples from + // a DEPTH_STENCIL_OES texture with a min-filter of LINEAR. Therefore we relax + // this restriction if WEBGL_depth_texture is enabled. + if (!mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) { + if (format->hasDepth && mTexCompareMode != LOCAL_GL_NONE) { + *out_reason = "A depth or depth-stencil format with TEXTURE_COMPARE_MODE" + " of NONE must have minification or magnification filtering" + " of NEAREST or NEAREST_MIPMAP_NEAREST."; + return false; + } + } + } + + // Texture completeness is effectively (though not explicitly) amended for GLES2 by + // the "Texture Access" section under $3.8 "Fragment Shaders". This also applies to + // vertex shaders, as noted on GLES 2.0.25, p41. + if (!mContext->IsWebGL2()) { + // GLES 2.0.25, p87-88: + // "Calling a sampler from a fragment shader will return (R,G,B,A)=(0,0,0,1) if + // any of the following conditions are true:" + + // "* A two-dimensional sampler is called, the minification filter is one that + // requires a mipmap[...], and the sampler's associated texture object is not + // complete[.]" + // (already covered) + + // "* A two-dimensional sampler is called, the minification filter is not one that + // requires a mipmap (either NEAREST nor[sic] LINEAR), and either dimension of + // the level zero array of the associated texture object is not positive." + // (already covered) + + // "* A two-dimensional sampler is called, the corresponding texture image is a + // non-power-of-two image[...], and either the texture wrap mode is not + // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR." + + // "* A cube map sampler is called, any of the corresponding texture images are + // non-power-of-two images, and either the texture wrap mode is not + // CLAMP_TO_EDGE, or the minification filter is neither NEAREST nor LINEAR." + if (!baseImageInfo.IsPowerOfTwo()) { + // "either the texture wrap mode is not CLAMP_TO_EDGE" + if (mWrapS != LOCAL_GL_CLAMP_TO_EDGE || + mWrapT != LOCAL_GL_CLAMP_TO_EDGE) + { + *out_reason = "Non-power-of-two textures must have a wrap mode of" + " CLAMP_TO_EDGE."; + return false; + } + + // "or the minification filter is neither NEAREST nor LINEAR" + if (requiresMipmap) { + *out_reason = "Mipmapping requires power-of-two textures."; + return false; + } + } + + // "* A cube map sampler is called, and either the corresponding cube map texture + // image is not cube complete, or TEXTURE_MIN_FILTER is one that requires a + // mipmap and the texture is not mipmap cube complete." + // (already covered) + } + return true; } -WebGLTextureFakeBlackStatus -WebGLTexture::ResolvedFakeBlackStatus() + +uint32_t +WebGLTexture::MaxEffectiveMipmapLevel() const { - if (MOZ_LIKELY(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown)) - return mFakeBlackStatus; - - // Determine if the texture needs to be faked as a black texture. - // See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec, and 3.8.13 in - // the OpenGL ES 3.0.4 spec. - if (!IsMipmapRangeValid()) { - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - return mFakeBlackStatus; - } - - for (size_t face = 0; face < mFacesCount; ++face) { - WebGLImageDataStatus status = ImageInfoAtFace(face, EffectiveBaseMipmapLevel()).mImageDataStatus; - if (status == WebGLImageDataStatus::NoImageData) { - // In case of undefined texture image, we don't print any message - // because this is a very common and often legitimate case - // (asynchronous texture loading). - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - return mFakeBlackStatus; - } - } - - const char preamble[] = "A texture is going to be rendered as if it were" - " black, as per the OpenGL ES 2.0.24 spec section" - " 3.8.2, because it"; - - if (mTarget == LOCAL_GL_TEXTURE_2D || - mTarget == LOCAL_GL_TEXTURE_3D) + if (mMinFilter == LOCAL_GL_NEAREST || + mMinFilter == LOCAL_GL_LINEAR) { - int dim = mTarget == LOCAL_GL_TEXTURE_2D ? 2 : 3; - if (DoesMinFilterRequireMipmap()) { - if (!IsMipmapComplete()) { - mContext->GenerateWarning("%s is a %dD texture, with a" - " minification filter requiring a" - " mipmap, and is not mipmap complete" - " (as defined in section 3.7.10).", - preamble, dim); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } else if (!mContext->IsWebGL2() && - !ImageInfoBase().IsPowerOfTwo()) - { - mContext->GenerateWarning("%s is a %dD texture, with a" - " minification filter requiring a" - " mipmap, and either its width or" - " height is not a power of two.", - preamble, dim); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } - } else { - // No mipmap required here. - if (!ImageInfoBase().IsPositive()) { - mContext->GenerateWarning("%s is a %dD texture and its width or" - " height is equal to zero.", - preamble, dim); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } else if (!AreBothWrapModesClampToEdge() && - !mContext->IsWebGL2() && - !ImageInfoBase().IsPowerOfTwo()) - { - mContext->GenerateWarning("%s is a %dD texture, with a" - " minification filter not requiring a" - " mipmap, with its width or height" - " not a power of two, and with a wrap" - " mode different from CLAMP_TO_EDGE.", - preamble, dim); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } - } - } else { - // Cube map - bool legalImageSize = true; - if (!mContext->IsWebGL2()) { - for (size_t face = 0; face < mFacesCount; ++face) - legalImageSize &= ImageInfoAtFace(face, 0).IsPowerOfTwo(); - } - - if (DoesMinFilterRequireMipmap()) { - if (!IsMipmapCubeComplete()) { - mContext->GenerateWarning("%s is a cube map texture, with a" - " minification filter requiring a" - " mipmap, and is not mipmap cube" - " complete (as defined in section" - " 3.7.10).", preamble); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } else if (!legalImageSize) { - mContext->GenerateWarning("%s is a cube map texture, with a" - " minification filter requiring a" - " mipmap, and either the width or the" - " height of some level 0 image is not" - " a power of two.", preamble); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } - } - else // no mipmap required - { - if (!IsCubeComplete()) { - mContext->GenerateWarning("%s is a cube map texture, with a" - " minification filter not requiring a" - " mipmap, and is not cube complete" - " (as defined in section 3.7.10).", - preamble); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } else if (!AreBothWrapModesClampToEdge() && !legalImageSize) { - mContext->GenerateWarning("%s is a cube map texture, with a" - " minification filter not requiring a" - " mipmap, with some level 0 image" - " having width or height not a power" - " of two, and with a wrap mode" - " different from CLAMP_TO_EDGE.", - preamble); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } - } + // No mips used. + return mBaseMipmapLevel; } - TexType type = TypeFromInternalFormat(ImageInfoBase().mEffectiveInternalFormat); + const auto& imageInfo = BaseImageInfo(); + MOZ_ASSERT(imageInfo.IsDefined()); - const char* badFormatText = nullptr; - const char* extText = nullptr; - - if (type == LOCAL_GL_FLOAT && - !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_float_linear)) - { - badFormatText = "FLOAT"; - extText = "OES_texture_float_linear"; - } else if (type == LOCAL_GL_HALF_FLOAT && - !Context()->IsExtensionEnabled(WebGLExtensionID::OES_texture_half_float_linear)) - { - badFormatText = "HALF_FLOAT"; - extText = "OES_texture_half_float_linear"; - } - - const char* badFilterText = nullptr; - if (badFormatText) { - if (mMinFilter == LOCAL_GL_LINEAR || - mMinFilter == LOCAL_GL_LINEAR_MIPMAP_LINEAR || - mMinFilter == LOCAL_GL_LINEAR_MIPMAP_NEAREST || - mMinFilter == LOCAL_GL_NEAREST_MIPMAP_LINEAR) - { - badFilterText = "minification"; - } else if (mMagFilter == LOCAL_GL_LINEAR) { - badFilterText = "magnification"; - } - } - - if (badFilterText) { - mContext->GenerateWarning("%s is a texture with a linear %s filter," - " which is not compatible with format %s by" - " default. Try enabling the %s extension, if" - " supported.", preamble, badFilterText, - badFormatText, extText); - mFakeBlackStatus = WebGLTextureFakeBlackStatus::IncompleteTexture; - } - - // We have exhausted all cases of incomplete textures, where we would need opaque black. - // We may still need transparent black in case of uninitialized image data. - bool hasUninitializedImageData = false; - for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { - for (size_t face = 0; face < mFacesCount; ++face) { - bool cur = (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::UninitializedImageData); - hasUninitializedImageData |= cur; - } - } - - if (hasUninitializedImageData) { - bool hasAnyInitializedImageData = false; - for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) { - for (size_t face = 0; face < mFacesCount; ++face) { - if (ImageInfoAtFace(face, level).mImageDataStatus == WebGLImageDataStatus::InitializedImageData) { - hasAnyInitializedImageData = true; - break; - } - } - if (hasAnyInitializedImageData) { - break; - } - } - - if (hasAnyInitializedImageData) { - /* The texture contains some initialized image data, and some - * uninitialized image data. In this case, we have no choice but to - * initialize all image data now. Fortunately, in this case we know - * that we can't be dealing with a depth texture per - * WEBGL_depth_texture and ANGLE_depth_texture (which allow only one - * image per texture) so we can assume that glTexImage2D is able to - * upload data to images. - */ - for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) - { - for (size_t face = 0; face < mFacesCount; ++face) { - TexImageTarget imageTarget = TexImageTargetForTargetAndFace(mTarget, - face); - const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); - if (imageInfo.mImageDataStatus == WebGLImageDataStatus::UninitializedImageData) - { - EnsureInitializedImageData(imageTarget, level); - } - } - } - mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; - } else { - // The texture only contains uninitialized image data. In this case, - // we can use a black texture for it. - mFakeBlackStatus = WebGLTextureFakeBlackStatus::UninitializedImageData; - } - } - - // we have exhausted all cases where we do need fakeblack, so if the status is still unknown, - // that means that we do NOT need it. - if (mFakeBlackStatus == WebGLTextureFakeBlackStatus::Unknown) { - mFakeBlackStatus = WebGLTextureFakeBlackStatus::NotNeeded; - } - - MOZ_ASSERT(mFakeBlackStatus != WebGLTextureFakeBlackStatus::Unknown); - return mFakeBlackStatus; + uint32_t maxLevelBySize = mBaseMipmapLevel + imageInfo.MaxMipmapLevels() - 1; + return std::min(maxLevelBySize, mMaxMipmapLevel); } -static bool -ClearByMask(WebGLContext* webgl, GLbitfield mask) -{ - gl::GLContext* gl = webgl->GL(); - MOZ_ASSERT(gl->IsCurrent()); - - GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); - if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) - return false; - - bool colorAttachmentsMask[WebGLContext::kMaxColorAttachments] = {false}; - if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { - colorAttachmentsMask[0] = true; - } - - webgl->ForceClearFramebufferWithDefaultValues(false, mask, colorAttachmentsMask); - return true; -} - -// `mask` from glClear. -static bool -ClearWithTempFB(WebGLContext* webgl, GLuint tex, - TexImageTarget texImageTarget, GLint level, - TexInternalFormat baseInternalFormat, - GLsizei width, GLsizei height) -{ - MOZ_ASSERT(texImageTarget == LOCAL_GL_TEXTURE_2D); - - gl::GLContext* gl = webgl->GL(); - MOZ_ASSERT(gl->IsCurrent()); - - gl::ScopedFramebuffer fb(gl); - gl::ScopedBindFramebuffer autoFB(gl, fb.FB()); - GLbitfield mask = 0; - - switch (baseInternalFormat.get()) { - case LOCAL_GL_LUMINANCE: - case LOCAL_GL_LUMINANCE_ALPHA: - case LOCAL_GL_ALPHA: - case LOCAL_GL_RGB: - case LOCAL_GL_RGBA: - case LOCAL_GL_BGR: - case LOCAL_GL_BGRA: - mask = LOCAL_GL_COLOR_BUFFER_BIT; - gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, - texImageTarget.get(), tex, level); - break; - case LOCAL_GL_DEPTH_COMPONENT32_OES: - case LOCAL_GL_DEPTH_COMPONENT24_OES: - case LOCAL_GL_DEPTH_COMPONENT16: - case LOCAL_GL_DEPTH_COMPONENT: - mask = LOCAL_GL_DEPTH_BUFFER_BIT; - gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, - texImageTarget.get(), tex, level); - break; - - case LOCAL_GL_DEPTH24_STENCIL8: - case LOCAL_GL_DEPTH_STENCIL: - mask = LOCAL_GL_DEPTH_BUFFER_BIT | - LOCAL_GL_STENCIL_BUFFER_BIT; - gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_DEPTH_ATTACHMENT, - texImageTarget.get(), tex, level); - gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_STENCIL_ATTACHMENT, - texImageTarget.get(), tex, level); - break; - - default: - return false; - } - MOZ_ASSERT(mask); - - if (ClearByMask(webgl, mask)) - return true; - - // Failed to simply build an FB from the tex, but maybe it needs a - // color buffer to be complete. - - if (mask & LOCAL_GL_COLOR_BUFFER_BIT) { - // Nope, it already had one. - return false; - } - - gl::ScopedRenderbuffer rb(gl); - { - // Only GLES guarantees RGBA4. - GLenum format = gl->IsGLES() ? LOCAL_GL_RGBA4 : LOCAL_GL_RGBA8; - gl::ScopedBindRenderbuffer rbBinding(gl, rb.RB()); - gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, width, height); - } - - gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, - LOCAL_GL_RENDERBUFFER, rb.RB()); - mask |= LOCAL_GL_COLOR_BUFFER_BIT; - - // Last chance! - return ClearByMask(webgl, mask); -} - - bool -WebGLTexture::EnsureInitializedImageData(TexImageTarget imageTarget, - GLint level) +WebGLTexture::GetFakeBlackType(const char* funcName, uint32_t texUnit, + FakeBlackType* const out_fakeBlack) { - const ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); - if (!imageInfo.HasUninitializedImageData()) + const char* incompleteReason; + if (!IsComplete(&incompleteReason)) { + if (incompleteReason) { + mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is" + " 'incomplete', and will be rendered as" + " RGBA(0,0,0,1), as per the GLES 2.0.24 $3.8.2: %s", + funcName, texUnit, mTarget.get(), + incompleteReason); + } + *out_fakeBlack = FakeBlackType::RGBA0001; return true; + } - mContext->MakeContextCurrent(); + // We may still want FakeBlack as an optimization for uninitialized image data. + bool hasUninitializedData = false; + bool hasInitializedData = false; - // Try to clear with glClear. - if (imageTarget == LOCAL_GL_TEXTURE_2D) { - bool cleared = ClearWithTempFB(mContext, mGLName, imageTarget, level, - imageInfo.mEffectiveInternalFormat, - imageInfo.mHeight, imageInfo.mWidth); - if (cleared) { - SetImageDataStatus(imageTarget, level, - WebGLImageDataStatus::InitializedImageData); + const auto maxLevel = MaxEffectiveMipmapLevel(); + MOZ_ASSERT(mBaseMipmapLevel <= maxLevel); + for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) { + for (uint8_t face = 0; face < mFaceCount; face++) { + const auto& cur = ImageInfoAtFace(face, level); + if (cur.IsDataInitialized()) + hasInitializedData = true; + else + hasUninitializedData = true; + } + } + MOZ_ASSERT(hasUninitializedData || hasInitializedData); + + if (!hasUninitializedData) { + *out_fakeBlack = FakeBlackType::None; + return true; + } + + if (!hasInitializedData) { + const auto format = ImageInfoAtFace(0, mBaseMipmapLevel).mFormat->format; + if (format->isColorFormat) { + *out_fakeBlack = (format->hasAlpha ? FakeBlackType::RGBA0000 + : FakeBlackType::RGBA0001); return true; } - } - // That didn't work. Try uploading zeros then. - size_t bitspertexel = GetBitsPerTexel(imageInfo.mEffectiveInternalFormat); - MOZ_ASSERT((bitspertexel % 8) == 0); // That would only happen for - // compressed images, which cannot use - // deferred initialization. - size_t bytespertexel = bitspertexel / 8; - CheckedUint32 checked_byteLength - = WebGLContext::GetImageSize( - imageInfo.mHeight, - imageInfo.mWidth, - imageInfo.mDepth, - bytespertexel, - mContext->mPixelStoreUnpackAlignment); - MOZ_RELEASE_ASSERT(checked_byteLength.isValid()); // Should have been checked earlier. - - size_t byteCount = checked_byteLength.value(); - - UniquePtr zeros((uint8_t*)moz_xcalloc(1, byteCount)); - if (zeros == nullptr) { - // Failed to allocate memory. Lose the context. Return OOM error. - mContext->ForceLoseContext(true); - mContext->ErrorOutOfMemory("EnsureInitializedImageData: Failed to alloc %u " - "bytes to clear image target `%s` level `%d`.", - byteCount, mContext->EnumName(imageTarget.get()), - level); - return false; - } - - gl::GLContext* gl = mContext->gl; - gl::ScopedBindTexture autoBindTex(gl, mGLName, mTarget); - - GLenum driverInternalFormat = LOCAL_GL_NONE; - GLenum driverFormat = LOCAL_GL_NONE; - GLenum driverType = LOCAL_GL_NONE; - DriverFormatsFromEffectiveInternalFormat(gl, - imageInfo.mEffectiveInternalFormat, - &driverInternalFormat, - &driverFormat, &driverType); - - mContext->GetAndFlushUnderlyingGLErrors(); - if (imageTarget == LOCAL_GL_TEXTURE_3D) { - MOZ_ASSERT(mImmutable, - "Shouldn't be possible to have non-immutable-format 3D" - " textures in WebGL"); - gl->fTexSubImage3D(imageTarget.get(), level, 0, 0, 0, imageInfo.mWidth, - imageInfo.mHeight, imageInfo.mDepth, driverFormat, - driverType, zeros.get()); + mContext->GenerateWarning("%s: Active texture %u for target 0x%04x is" + " uninitialized, and will be (perhaps slowly) cleared" + " by the implementation.", + funcName, texUnit, mTarget.get()); } else { - if (mImmutable) { - gl->fTexSubImage2D(imageTarget.get(), level, 0, 0, imageInfo.mWidth, - imageInfo.mHeight, driverFormat, driverType, - zeros.get()); - } else { - gl->fTexImage2D(imageTarget.get(), level, driverInternalFormat, - imageInfo.mWidth, imageInfo.mHeight, 0, - driverFormat, driverType, zeros.get()); + mContext->GenerateWarning("%s: Active texture %u for target 0x%04x contains" + " TexImages with uninitialized data along with" + " TexImages with initialized data, forcing the" + " implementation to (slowly) initialize the" + " uninitialized TexImages.", + funcName, texUnit, mTarget.get()); + } + + GLenum baseImageTarget = mTarget.get(); + if (baseImageTarget == LOCAL_GL_TEXTURE_CUBE_MAP) + baseImageTarget = LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; + + for (uint32_t level = mBaseMipmapLevel; level <= maxLevel; level++) { + for (uint8_t face = 0; face < mFaceCount; face++) { + TexImageTarget imageTarget = baseImageTarget + face; + if (!EnsureImageDataInitialized(funcName, imageTarget, level)) + return false; // The world just exploded. } } - GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); - if (error) { - // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover - // from this here. - gfxCriticalError() << "GL context GetAndFlushUnderlyingGLErrors " << gfx::hexa(error); - printf_stderr("Error: 0x%4x\n", error); - if (error != LOCAL_GL_OUT_OF_MEMORY) { - // Errors on texture upload have been related to video - // memory exposure in the past, which is a security issue. - // Force loss of context. - mContext->ForceLoseContext(true); + + *out_fakeBlack = FakeBlackType::None; + return true; +} + +static void +SetSwizzle(gl::GLContext* gl, TexTarget target, const GLint* swizzle) +{ + static const GLint kNoSwizzle[4] = { LOCAL_GL_RED, LOCAL_GL_GREEN, LOCAL_GL_BLUE, + LOCAL_GL_ALPHA }; + if (!swizzle) { + swizzle = kNoSwizzle; + } else if (!gl->IsSupported(gl::GLFeature::texture_swizzle)) { + MOZ_CRASH("Needs swizzle feature to swizzle!"); + } + + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, swizzle[0]); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, swizzle[1]); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, swizzle[2]); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, swizzle[3]); +} + +bool +WebGLTexture::ResolveForDraw(const char* funcName, uint32_t texUnit, + FakeBlackType* const out_fakeBlack) +{ + if (!mIsResolved) { + if (!GetFakeBlackType(funcName, texUnit, &mResolved_FakeBlack)) return false; + + // Check which swizzle we should use. Since the texture must be complete at this + // point, just grab the format off any valid image. + const GLint* newSwizzle = nullptr; + if (mResolved_FakeBlack == FakeBlackType::None) { + const auto& cur = ImageInfoAtFace(0, mBaseMipmapLevel); + newSwizzle = cur.mFormat->textureSwizzleRGBA; } - // Out-of-memory uploading pixels to GL. Lose context and report OOM. - mContext->ForceLoseContext(true); - mContext->ErrorOutOfMemory("EnsureNoUninitializedImageData: Failed to " - "upload texture of width: %u, height: %u, " - "depth: %u to target %s level %d.", - imageInfo.mWidth, imageInfo.mHeight, imageInfo.mDepth, - mContext->EnumName(imageTarget.get()), level); + // Only set the swizzle if it changed since last time we did it. + if (newSwizzle != mResolved_Swizzle) { + mResolved_Swizzle = newSwizzle; + + // Set the new swizzle! + mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + texUnit); + SetSwizzle(mContext->gl, mTarget, mResolved_Swizzle); + mContext->gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mContext->mActiveTexture); + } + + mIsResolved = true; + } + + *out_fakeBlack = mResolved_FakeBlack; + return true; +} + +bool +WebGLTexture::EnsureImageDataInitialized(const char* funcName, TexImageTarget target, + uint32_t level) +{ + auto& imageInfo = ImageInfoAt(target, level); + MOZ_ASSERT(imageInfo.IsDefined()); + + if (imageInfo.IsDataInitialized()) + return true; + + return InitializeImageData(funcName, target, level); +} + +bool +WebGLTexture::InitializeImageData(const char* funcName, TexImageTarget target, + uint32_t level) +{ + auto& imageInfo = ImageInfoAt(target, level); + MOZ_ASSERT(imageInfo.IsDefined()); + MOZ_ASSERT(!imageInfo.IsDataInitialized()); + + const bool respecifyTexture = false; + const auto& usage = imageInfo.mFormat; + const auto& width = imageInfo.mWidth; + const auto& height = imageInfo.mHeight; + const auto& depth = imageInfo.mDepth; + + if (!ZeroTextureData(mContext, funcName, respecifyTexture, target, level, usage, 0, 0, + 0, width, height, depth)) + { return false; } - SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData); - + imageInfo.SetIsDataInitialized(true, this); return true; } void -WebGLTexture::SetFakeBlackStatus(WebGLTextureFakeBlackStatus x) +WebGLTexture::ClampLevelBaseAndMax() { - mFakeBlackStatus = x; - mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown); + if (!mImmutable) + return; + + // GLES 3.0.4, p158: + // "For immutable-format textures, `level_base` is clamped to the range + // `[0, levels-1]`, `level_max` is then clamped to the range ` + // `[level_base, levels-1]`, where `levels` is the parameter passed to + // TexStorage* for the texture object." + mBaseMipmapLevel = Clamp(mBaseMipmapLevel, 0, mImmutableLevelCount - 1); + mMaxMipmapLevel = Clamp(mMaxMipmapLevel, mBaseMipmapLevel, + mImmutableLevelCount - 1); +} + +void +WebGLTexture::PopulateMipChain(uint32_t firstLevel, uint32_t lastLevel) +{ + const ImageInfo& baseImageInfo = ImageInfoAtFace(0, firstLevel); + MOZ_ASSERT(baseImageInfo.IsDefined()); + + uint32_t refWidth = baseImageInfo.mWidth; + uint32_t refHeight = baseImageInfo.mHeight; + uint32_t refDepth = baseImageInfo.mDepth; + if (!refWidth || !refHeight || !refDepth) + return; + + for (uint32_t level = firstLevel + 1; level <= lastLevel; level++) { + bool isMinimal = (refWidth == 1 && + refHeight == 1); + if (mTarget == LOCAL_GL_TEXTURE_3D) { + isMinimal &= (refDepth == 1); + } + + // Higher levels are unaffected. + if (isMinimal) + break; + + refWidth = std::max(uint32_t(1), refWidth / 2); + refHeight = std::max(uint32_t(1), refHeight / 2); + if (mTarget == LOCAL_GL_TEXTURE_3D) { // But not TEXTURE_2D_ARRAY! + refDepth = std::max(uint32_t(1), refDepth / 2); + } + + const ImageInfo cur(baseImageInfo.mFormat, refWidth, refHeight, refDepth, + baseImageInfo.IsDataInitialized()); + + SetImageInfosAtLevel(level, cur); + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -739,50 +660,79 @@ WebGLTexture::BindTexture(TexTarget texTarget) if (IsDeleted()) return false; - if (HasEverBeenBound() && mTarget != texTarget) { - mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target"); + const bool isFirstBinding = !HasEverBeenBound(); + if (!isFirstBinding && mTarget != texTarget) { + mContext->ErrorInvalidOperation("bindTexture: This texture has already been bound" + " to a different target."); return false; } - mContext->SetFakeBlackStatus(WebGLContextFakeBlackStatus::Unknown); - Bind(texTarget); + mTarget = texTarget; + + mContext->gl->fBindTexture(mTarget.get(), mGLName); + + if (isFirstBinding) { + mFaceCount = IsCubeMap() ? 6 : 1; + + gl::GLContext* gl = mContext->gl; + + // Thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R + // is not present in GLES 2, but is present in GL and it seems as if for + // cube maps we need to set it to GL_CLAMP_TO_EDGE to get the expected + // GLES behavior. + // If we are WebGL 2 though, we'll want to leave it as REPEAT. + const bool hasWrapR = gl->IsSupported(gl::GLFeature::texture_3D); + if (IsCubeMap() && hasWrapR && !mContext->IsWebGL2()) { + gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_WRAP_R, + LOCAL_GL_CLAMP_TO_EDGE); + } + } + return true; } + void WebGLTexture::GenerateMipmap(TexTarget texTarget) { - const TexImageTarget imageTarget = (texTarget == LOCAL_GL_TEXTURE_2D) - ? LOCAL_GL_TEXTURE_2D - : LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; - if (!IsMipmapRangeValid()) - { - return mContext->ErrorInvalidOperation("generateMipmap: Texture does not have a valid mipmap range."); - } - if (!HasImageInfoAt(imageTarget, EffectiveBaseMipmapLevel())) - { - return mContext->ErrorInvalidOperation("generateMipmap: Level zero of texture is not defined."); + // GLES 3.0.4 p160: + // "Mipmap generation replaces texel array levels level base + 1 through q with arrays + // derived from the level base array, regardless of their previous contents. All + // other mipmap arrays, including the level base array, are left unchanged by this + // computation." + const ImageInfo& baseImageInfo = BaseImageInfo(); + if (!baseImageInfo.IsDefined()) { + mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture is" + " not defined."); + return; } - if (!mContext->IsWebGL2() && !IsFirstImagePowerOfTwo()) - return mContext->ErrorInvalidOperation("generateMipmap: Level zero of texture does not have power-of-two width and height."); - - TexInternalFormat internalformat = ImageInfoAt(imageTarget, 0).EffectiveInternalFormat(); - if (IsTextureFormatCompressed(internalformat)) - return mContext->ErrorInvalidOperation("generateMipmap: Texture data at level zero is compressed."); - - if (mContext->IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture) && - (IsGLDepthFormat(internalformat) || IsGLDepthStencilFormat(internalformat))) - { - return mContext->ErrorInvalidOperation("generateMipmap: " - "A texture that has a base internal format of " - "DEPTH_COMPONENT or DEPTH_STENCIL isn't supported"); + if (IsCubeMap() && !IsCubeComplete()) { + mContext->ErrorInvalidOperation("generateMipmap: Cube maps must be \"cube" + " complete\"."); + return; } - if (!AreAllLevel0ImageInfosEqual()) - return mContext->ErrorInvalidOperation("generateMipmap: The six faces of this cube map have different dimensions, format, or type."); + if (!mContext->IsWebGL2() && !baseImageInfo.IsPowerOfTwo()) { + mContext->ErrorInvalidOperation("generateMipmap: The base level of the texture" + " does not have power-of-two dimensions."); + return; + } - SetGeneratedMipmap(); + auto format = baseImageInfo.mFormat->format; + if (format->compression) { + mContext->ErrorInvalidOperation("generateMipmap: Texture data at base level is" + " compressed."); + return; + } + + if (format->hasDepth) { + mContext->ErrorInvalidOperation("generateMipmap: Depth textures are not" + " supported."); + return; + } + + // Done with validation. Do the operation. mContext->MakeContextCurrent(); gl::GLContext* gl = mContext->gl; @@ -793,12 +743,20 @@ WebGLTexture::GenerateMipmap(TexTarget texTarget) // overhead so we do it unconditionally. // // note that the choice of GL_NEAREST_MIPMAP_NEAREST really matters. See Chromium bug 101105. - gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST_MIPMAP_NEAREST); + gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, + LOCAL_GL_NEAREST_MIPMAP_NEAREST); gl->fGenerateMipmap(texTarget.get()); - gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, MinFilter().get()); + gl->fTexParameteri(texTarget.get(), LOCAL_GL_TEXTURE_MIN_FILTER, + mMinFilter.get()); } else { gl->fGenerateMipmap(texTarget.get()); } + + // Record the results. + // Note that we don't use MaxEffectiveMipmapLevel() here, since that returns + // mBaseMipmapLevel if the min filter doesn't require mipmaps. + const uint32_t lastLevel = mBaseMipmapLevel + baseImageInfo.MaxMipmapLevels() - 1; + PopulateMipChain(mBaseMipmapLevel, lastLevel); } JS::Value @@ -817,7 +775,6 @@ WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname) case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_COMPARE_FUNC: case LOCAL_GL_TEXTURE_COMPARE_MODE: - case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: case LOCAL_GL_TEXTURE_IMMUTABLE_LEVELS: case LOCAL_GL_TEXTURE_MAX_LEVEL: case LOCAL_GL_TEXTURE_SWIZZLE_A: @@ -828,6 +785,10 @@ WebGLTexture::GetTexParameter(TexTarget texTarget, GLenum pname) mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i); return JS::NumberValue(uint32_t(i)); + case LOCAL_GL_TEXTURE_IMMUTABLE_FORMAT: + mContext->gl->fGetTexParameteriv(texTarget.get(), pname, &i); + return JS::BooleanValue(bool(i)); + case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: case LOCAL_GL_TEXTURE_MAX_LOD: case LOCAL_GL_TEXTURE_MIN_LOD: @@ -857,41 +818,57 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar GLint intParam = maybeIntParam ? *maybeIntParam : GLint(*maybeFloatParam); GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam); + bool isPNameValid = false; + switch (pname) { + // GLES 2.0.25 p76: + case LOCAL_GL_TEXTURE_WRAP_S: + case LOCAL_GL_TEXTURE_WRAP_T: + case LOCAL_GL_TEXTURE_MIN_FILTER: + case LOCAL_GL_TEXTURE_MAG_FILTER: + isPNameValid = true; + break; + + // GLES 3.0.4 p149-150: + case LOCAL_GL_TEXTURE_BASE_LEVEL: + case LOCAL_GL_TEXTURE_COMPARE_MODE: + case LOCAL_GL_TEXTURE_COMPARE_FUNC: + case LOCAL_GL_TEXTURE_MAX_LEVEL: + case LOCAL_GL_TEXTURE_MAX_LOD: + case LOCAL_GL_TEXTURE_MIN_LOD: + case LOCAL_GL_TEXTURE_WRAP_R: + if (mContext->IsWebGL2()) + isPNameValid = true; + break; + + case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: + if (mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) + isPNameValid = true; + break; + } + + if (!isPNameValid) { + mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); + return; + } + + //////////////// + // Validate params and invalidate if needed. + bool paramBadEnum = false; bool paramBadValue = false; switch (pname) { case LOCAL_GL_TEXTURE_BASE_LEVEL: case LOCAL_GL_TEXTURE_MAX_LEVEL: - if (!mContext->IsWebGL2()) - return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); - - if (intParam < 0) { - paramBadValue = true; - break; - } - - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - - if (pname == LOCAL_GL_TEXTURE_BASE_LEVEL) - mBaseMipmapLevel = intParam; - else - mMaxMipmapLevel = intParam; - + paramBadValue = (intParam < 0); break; case LOCAL_GL_TEXTURE_COMPARE_MODE: - if (!mContext->IsWebGL2()) - return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); - paramBadValue = (intParam != LOCAL_GL_NONE && intParam != LOCAL_GL_COMPARE_REF_TO_TEXTURE); break; case LOCAL_GL_TEXTURE_COMPARE_FUNC: - if (!mContext->IsWebGL2()) - return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); - switch (intParam) { case LOCAL_GL_LEQUAL: case LOCAL_GL_GEQUAL: @@ -905,6 +882,7 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar default: paramBadValue = true; + break; } break; @@ -916,12 +894,11 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar case LOCAL_GL_LINEAR_MIPMAP_NEAREST: case LOCAL_GL_NEAREST_MIPMAP_LINEAR: case LOCAL_GL_LINEAR_MIPMAP_LINEAR: - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - mMinFilter = intParam; break; default: paramBadEnum = true; + break; } break; @@ -929,56 +906,36 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar switch (intParam) { case LOCAL_GL_NEAREST: case LOCAL_GL_LINEAR: - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - mMagFilter = intParam; break; default: paramBadEnum = true; + break; } break; case LOCAL_GL_TEXTURE_WRAP_S: - switch (intParam) { - case LOCAL_GL_CLAMP_TO_EDGE: - case LOCAL_GL_MIRRORED_REPEAT: - case LOCAL_GL_REPEAT: - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - mWrapS = intParam; - break; - - default: - paramBadEnum = true; - } - break; - case LOCAL_GL_TEXTURE_WRAP_T: + case LOCAL_GL_TEXTURE_WRAP_R: switch (intParam) { case LOCAL_GL_CLAMP_TO_EDGE: case LOCAL_GL_MIRRORED_REPEAT: case LOCAL_GL_REPEAT: - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - mWrapT = intParam; break; default: paramBadEnum = true; + break; } break; case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: - if (!mContext->IsExtensionEnabled(WebGLExtensionID::EXT_texture_filter_anisotropic)) - return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); - if (maybeFloatParam && floatParam < 1.0f) paramBadValue = true; else if (maybeIntParam && intParam < 1) paramBadValue = true; break; - - default: - return mContext->ErrorInvalidEnumInfo("texParameter: pname", pname); } if (paramBadEnum) { @@ -1005,6 +962,53 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar return; } + //////////////// + // Store any needed values + + switch (pname) { + case LOCAL_GL_TEXTURE_BASE_LEVEL: + mBaseMipmapLevel = intParam; + ClampLevelBaseAndMax(); + break; + + case LOCAL_GL_TEXTURE_MAX_LEVEL: + mMaxMipmapLevel = intParam; + ClampLevelBaseAndMax(); + break; + + case LOCAL_GL_TEXTURE_MIN_FILTER: + mMinFilter = intParam; + break; + + case LOCAL_GL_TEXTURE_MAG_FILTER: + mMagFilter = intParam; + break; + + case LOCAL_GL_TEXTURE_WRAP_S: + mWrapS = intParam; + break; + + case LOCAL_GL_TEXTURE_WRAP_T: + mWrapT = intParam; + break; + + // We don't actually need to store the WRAP_R, since it doesn't change texture + // completeness rules. + } + + // Only a couple of pnames don't need to invalidate our resolve status cache. + switch (pname) { + case LOCAL_GL_TEXTURE_MAX_ANISOTROPY_EXT: + case LOCAL_GL_TEXTURE_WRAP_R: + break; + + default: + InvalidateResolveCache(); + break; + } + + //////////////// + mContext->MakeContextCurrent(); if (maybeIntParam) mContext->gl->fTexParameteri(texTarget.get(), pname, intParam); @@ -1012,6 +1016,8 @@ WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntPar mContext->gl->fTexParameterf(texTarget.get(), pname, floatParam); } +//////////////////////////////////////////////////////////////////////////////// + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTexture) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTexture, AddRef) diff --git a/dom/canvas/WebGLTexture.h b/dom/canvas/WebGLTexture.h index 66a5a238ca..da29cecc9f 100644 --- a/dom/canvas/WebGLTexture.h +++ b/dom/canvas/WebGLTexture.h @@ -7,6 +7,7 @@ #define WEBGL_TEXTURE_H_ #include +#include #include "mozilla/Assertions.h" #include "mozilla/CheckedInt.h" @@ -20,6 +21,7 @@ namespace mozilla { class ErrorResult; +class WebGLContext; namespace dom { class Element; @@ -27,18 +29,16 @@ class ImageData; class ArrayBufferViewOrSharedArrayBufferView; } // namespace dom -// Zero is not an integer power of two. -inline bool -IsPOTAssumingNonnegative(GLsizei x) -{ - MOZ_ASSERT(x >= 0); - return x && (x & (x-1)) == 0; -} +namespace webgl { +class TexUnpackBlob; +} // namespace webgl + bool DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t dims, const char* funcName); + // NOTE: When this class is switched to new DOM bindings, update the (then-slow) // WrapObject calls in GetParameter and GetFramebufferAttachmentParameter. class WebGLTexture final @@ -46,33 +46,145 @@ class WebGLTexture final , public WebGLRefCountedObject , public LinkedListElement , public WebGLContextBoundObject - , public WebGLFramebufferAttachable { + // Friends friend class WebGLContext; friend class WebGLFramebuffer; + //////////////////////////////////// + // Members public: - class ImageInfo; - const GLuint mGLName; protected: - GLenum mTarget; + TexTarget mTarget; + + static const uint8_t kMaxFaceCount = 6; + uint8_t mFaceCount; // 6 for cube maps, 1 otherwise. + TexMinFilter mMinFilter; TexMagFilter mMagFilter; TexWrap mWrapS, mWrapT; - size_t mFacesCount, mMaxLevelWithCustomImages; - nsTArray mImageInfos; - - bool mHaveGeneratedMipmap; // Set by generateMipmap bool mImmutable; // Set by texStorage* + uint8_t mImmutableLevelCount; - size_t mBaseMipmapLevel; // Set by texParameter (defaults to 0) - size_t mMaxMipmapLevel; // Set by texParameter (defaults to 1000) + uint32_t mBaseMipmapLevel; // Set by texParameter (defaults to 0) + uint32_t mMaxMipmapLevel; // Set by texParameter (defaults to 1000) + // You almost certainly don't want to query mMaxMipmapLevel. + // You almost certainly want MaxEffectiveMipmapLevel(). - WebGLTextureFakeBlackStatus mFakeBlackStatus; + GLenum mTexCompareMode; + // Resolvable optimizations: + bool mIsResolved; + FakeBlackType mResolved_FakeBlack; + const GLint* mResolved_Swizzle; // nullptr means 'default swizzle'. + +public: + class ImageInfo; + + // numLevels = log2(size) + 1 + // numLevels(16k) = log2(16k) + 1 = 14 + 1 = 15 + // numLevels(1M) = log2(1M) + 1 = 19.9 + 1 ~= 21 + // Or we can just max this out to 31, which is the number of unsigned bits in GLsizei. + static const uint8_t kMaxLevelCount = 31; + + // And in turn, it needs these forwards: +protected: + // We need to forward these. + void SetImageInfo(ImageInfo* target, const ImageInfo& newInfo); + void SetImageInfosAtLevel(uint32_t level, const ImageInfo& newInfo); + +public: + // We store information about the various images that are part of this + // texture. (cubemap faces, mipmap levels) + class ImageInfo + { + friend void WebGLTexture::SetImageInfo(ImageInfo* target, + const ImageInfo& newInfo); + friend void WebGLTexture::SetImageInfosAtLevel(uint32_t level, + const ImageInfo& newInfo); + + public: + static const ImageInfo kUndefined; + + // This is the "effective internal format" of the texture, an official + // OpenGL spec concept, see OpenGL ES 3.0.3 spec, section 3.8.3, page + // 126 and below. + const webgl::FormatUsageInfo* const mFormat; + + const uint32_t mWidth; + const uint32_t mHeight; + const uint32_t mDepth; + + protected: + bool mIsDataInitialized; + + std::set mAttachPoints; + + public: + ImageInfo() + : mFormat(LOCAL_GL_NONE) + , mWidth(0) + , mHeight(0) + , mDepth(0) + , mIsDataInitialized(false) + { } + + ImageInfo(const webgl::FormatUsageInfo* format, uint32_t width, uint32_t height, + uint32_t depth, bool isDataInitialized) + : mFormat(format) + , mWidth(width) + , mHeight(height) + , mDepth(depth) + , mIsDataInitialized(isDataInitialized) + { + MOZ_ASSERT(mFormat); + } + + void Clear(); + + ~ImageInfo() { + if (!IsDefined()) + Clear(); + } + + protected: + ImageInfo& operator =(const ImageInfo& a); + + public: + uint32_t MaxMipmapLevels() const { + // GLES 3.0.4, 3.8 - Mipmapping: `floor(log2(largest_of_dims)) + 1` + uint32_t largest = std::max(std::max(mWidth, mHeight), mDepth); + return FloorLog2Size(largest) + 1; + } + + bool IsPowerOfTwo() const; + + void AddAttachPoint(WebGLFBAttachPoint* attachPoint); + void RemoveAttachPoint(WebGLFBAttachPoint* attachPoint); + void OnRespecify() const; + + size_t MemoryUsage() const; + + bool IsDefined() const { + if (mFormat == LOCAL_GL_NONE) { + MOZ_ASSERT(!mWidth && !mHeight && !mDepth); + return false; + } + + return true; + } + + bool IsDataInitialized() const { return mIsDataInitialized; } + + void SetIsDataInitialized(bool isDataInitialized, WebGLTexture* tex); + }; + + ImageInfo mImageInfoArr[kMaxLevelCount * kMaxFaceCount]; + + //////////////////////////////////// public: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTexture) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTexture) @@ -82,10 +194,10 @@ public: void Delete(); bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; } - GLenum Target() const { return mTarget; } + TexTarget Target() const { return mTarget; } WebGLContext* GetParentObject() const { - return Context(); + return mContext; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle givenProto) override; @@ -94,6 +206,7 @@ protected: ~WebGLTexture() { DeleteOnce(); } + public: //////////////////////////////////// // GL calls @@ -107,290 +220,129 @@ public: //////////////////////////////////// // WebGLTextureUpload.cpp - void CompressedTexImage2D(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLsizei width, GLsizei height, - GLint border, const dom::ArrayBufferViewOrSharedArrayBufferView& view); + void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset, + GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLenum unpackFormat, GLenum unpackType, + const dom::Nullable& maybeView); - void CompressedTexImage3D(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLsizei width, GLsizei height, - GLsizei depth, GLint border, GLsizei imageSize, - const dom::ArrayBufferViewOrSharedArrayBufferView& view); + void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset, + GLint zOffset, GLenum unpackFormat, GLenum unpackType, + dom::ImageData* imageData); - - void CompressedTexSubImage2D(TexImageTarget texImageTarget, GLint level, - GLint xOffset, GLint yOffset, GLsizei width, - GLsizei height, GLenum unpackFormat, - const dom::ArrayBufferViewOrSharedArrayBufferView& view); - - void CompressedTexSubImage3D(TexImageTarget texImageTarget, GLint level, - GLint xOffset, GLint yOffset, GLint zOffset, - GLsizei width, GLsizei height, GLsizei depth, - GLenum unpackFormat, GLsizei imageSize, - const dom::ArrayBufferViewOrSharedArrayBufferView& view); - - - void CopyTexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, - GLint x, GLint y, GLsizei width, GLsizei height, GLint border); - - - void CopyTexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLint x, GLint y, GLsizei width, - GLsizei height); - - void CopyTexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width, - GLsizei height); - - - void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, - GLsizei width, GLsizei height, GLint border, GLenum unpackFormat, - GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv); - void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, - GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, - ErrorResult* const out_rv); - void TexImage2D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, - GLenum unpackFormat, GLenum unpackType, dom::Element* elem, - ErrorResult* const out_rv); - - void TexImage3D(TexImageTarget target, GLint level, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, GLint border, - GLenum unpackFormat, GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv); - - - void TexStorage2D(TexTarget texTarget, GLsizei levels, GLenum internalFormat, - GLsizei width, GLsizei height); - void TexStorage3D(TexTarget texTarget, GLsizei levels, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth); - - - void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLsizei width, GLsizei height, GLenum unpackFormat, - GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv); - void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLenum unpackFormat, GLenum unpackType, - dom::ImageData* imageData, ErrorResult* const out_rv); - void TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLenum unpackFormat, GLenum unpackType, - dom::Element* elem, ErrorResult* const out_rv); - - void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, - GLsizei depth, GLenum unpackFormat, GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv); - void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLint zOffset, GLenum unpackFormat, - GLenum unpackType, dom::ImageData* imageData, - ErrorResult* const out_rv); - void TexSubImage3D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLint zOffset, GLenum unpackFormat, - GLenum unpackType, dom::Element* elem, ErrorResult* const out_rv); + void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset, + GLint zOffset, GLenum unpackFormat, GLenum unpackType, + dom::Element* elem, ErrorResult* const out_error); protected: + void TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, GLint yOffset, + GLint zOffset, GLint border, GLenum unpackFormat, + GLenum unpackType, webgl::TexUnpackBlob* blob); - /** Like glTexImage2D, but if the call may change the texture size, checks - * any GL error generated by this glTexImage2D call and returns it. - */ - GLenum CheckedTexImage2D(TexImageTarget texImageTarget, GLint level, - TexInternalFormat internalFormat, GLsizei width, - GLsizei height, GLint border, TexFormat format, - TexType type, const GLvoid* data); - - bool ValidateTexStorage(TexImageTarget texImageTarget, GLsizei levels, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, - const char* funcName); - void SpecifyTexStorage(GLsizei levels, TexInternalFormat internalFormat, - GLsizei width, GLsizei height, GLsizei depth); - - void CopyTexSubImage2D_base(TexImageTarget texImageTarget, - GLint level, TexInternalFormat internalFormat, - GLint xoffset, GLint yoffset, GLint x, GLint y, - GLsizei width, GLsizei height, bool isSub); - - bool TexImageFromVideoElement(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLenum unpackFormat, - GLenum unpackType, dom::Element* elem); - - // If jsArrayType is MaxTypedArrayViewType, it means no array. - void TexImage2D_base(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLsizei width, GLsizei height, - GLsizei srcStrideOrZero, GLint border, GLenum unpackFormat, - GLenum unpackType, void* data, uint32_t byteLength, - js::Scalar::Type jsArrayType, WebGLTexelFormat srcFormat, - bool srcPremultiplied); - void TexSubImage2D_base(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLsizei width, GLsizei height, - GLsizei srcStrideOrZero, GLenum unpackFormat, - GLenum unpackType, void* pixels, uint32_t byteLength, - js::Scalar::Type jsArrayType, WebGLTexelFormat srcFormat, - bool srcPremultiplied); - - bool ValidateTexStorage(TexTarget texTarget, GLsizei levels, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, - const char* info); - bool ValidateSizedInternalFormat(GLenum internalFormat, const char* info); - + bool ValidateTexImageSpecification(const char* funcName, TexImageTarget target, + GLint level, GLsizei width, GLsizei height, + GLsizei depth, GLint border, + WebGLTexture::ImageInfo** const out_imageInfo); + bool ValidateTexImageSelection(const char* funcName, TexImageTarget target, + GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, + WebGLTexture::ImageInfo** const out_imageInfo); public: - // We store information about the various images that are part of this - // texture. (cubemap faces, mipmap levels) - class ImageInfo - : public WebGLRectangleObject - { - public: - ImageInfo() - : mEffectiveInternalFormat(LOCAL_GL_NONE) - , mDepth(0) - , mImageDataStatus(WebGLImageDataStatus::NoImageData) - {} + void TexStorage(const char* funcName, TexTarget target, GLsizei levels, + GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth); +protected: + void TexImage(const char* funcName, TexImageTarget target, GLint level, + GLenum internalFormat, GLint border, GLenum unpackFormat, + GLenum unpackType, webgl::TexUnpackBlob* blob); + void TexSubImage(const char* funcName, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLenum unpackFormat, + GLenum unpackType, webgl::TexUnpackBlob* blob); +public: + void CompressedTexImage(const char* funcName, TexImageTarget target, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, + const dom::ArrayBufferViewOrSharedArrayBufferView& view); + void CompressedTexSubImage(const char* funcName, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum sizedUnpackFormat, + const dom::ArrayBufferViewOrSharedArrayBufferView& view); + void CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat, + GLint x, GLint y, GLsizei width, GLsizei height, GLint border); + void CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLint x, GLint y, + GLsizei width, GLsizei height); - ImageInfo(GLsizei width, GLsizei height, GLsizei depth, - TexInternalFormat effectiveInternalFormat, - WebGLImageDataStatus status) - : WebGLRectangleObject(width, height) - , mEffectiveInternalFormat(effectiveInternalFormat) - , mDepth(depth) - , mImageDataStatus(status) - { - // shouldn't use this constructor to construct a null ImageInfo - MOZ_ASSERT(status != WebGLImageDataStatus::NoImageData); - } + //////////////////////////////////// - bool operator==(const ImageInfo& a) const { - return mImageDataStatus == a.mImageDataStatus && - mWidth == a.mWidth && - mHeight == a.mHeight && - mDepth == a.mDepth && - mEffectiveInternalFormat == a.mEffectiveInternalFormat; - } - bool operator!=(const ImageInfo& a) const { - return !(*this == a); - } - bool IsSquare() const { - return mWidth == mHeight; - } - bool IsPositive() const { - return mWidth > 0 && mHeight > 0 && mDepth > 0; - } - bool IsPowerOfTwo() const { - MOZ_ASSERT(mWidth >= 0); - MOZ_ASSERT(mHeight >= 0); - return IsPOTAssumingNonnegative(mWidth) && - IsPOTAssumingNonnegative(mHeight); - } - bool HasUninitializedImageData() const { - return mImageDataStatus == WebGLImageDataStatus::UninitializedImageData; - } - size_t MemoryUsage() const; +protected: + void ClampLevelBaseAndMax(); - TexInternalFormat EffectiveInternalFormat() const { - return mEffectiveInternalFormat; - } - GLsizei Depth() const { return mDepth; } + void PopulateMipChain(uint32_t baseLevel, uint32_t maxLevel); - protected: - // This is the "effective internal format" of the texture, an official - // OpenGL spec concept, see OpenGL ES 3.0.3 spec, section 3.8.3, page - // 126 and below. - TexInternalFormat mEffectiveInternalFormat; + uint32_t MaxEffectiveMipmapLevel() const; - /* Used only for 3D textures. - * Note that mWidth and mHeight are inherited from WebGLRectangleObject. - * It's a pity to store a useless mDepth on non-3D texture images, but - * the size of GLsizei is negligible compared to the typical size of a texture image. - */ - GLsizei mDepth; + static uint8_t FaceForTarget(TexImageTarget texImageTarget) { + GLenum rawTexImageTarget = texImageTarget.get(); + switch (rawTexImageTarget) { + case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: + return rawTexImageTarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; - WebGLImageDataStatus mImageDataStatus; - - friend class WebGLTexture; - }; - -private: - static size_t FaceForTarget(TexImageTarget texImageTarget) { - if (texImageTarget == LOCAL_GL_TEXTURE_2D || - texImageTarget == LOCAL_GL_TEXTURE_3D) - { + default: return 0; } - return texImageTarget.get() - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X; } - ImageInfo& ImageInfoAtFace(size_t face, GLint level) { - MOZ_ASSERT(face < mFacesCount, - "Wrong face index, must be 0 for TEXTURE_2D or TEXTURE_3D," - " and at most 5 for cube maps."); - - // No need to check level as a wrong value would be caught by - // ElementAt(). - return mImageInfos.ElementAt(level * mFacesCount + face); + ImageInfo& ImageInfoAtFace(uint8_t face, uint32_t level) { + MOZ_ASSERT(face < mFaceCount); + MOZ_ASSERT(level < kMaxLevelCount); + size_t pos = (level * mFaceCount) + face; + return mImageInfoArr[pos]; } - const ImageInfo& ImageInfoAtFace(size_t face, GLint level) const { - return const_cast( - const_cast(this)->ImageInfoAtFace(face, level) - ); + const ImageInfo& ImageInfoAtFace(uint8_t face, uint32_t level) const { + return const_cast(this)->ImageInfoAtFace(face, level); } public: - ImageInfo& ImageInfoAt(TexImageTarget imageTarget, GLint level) { - size_t face = FaceForTarget(imageTarget); + ImageInfo& ImageInfoAt(TexImageTarget texImageTarget, GLint level) { + auto face = FaceForTarget(texImageTarget); return ImageInfoAtFace(face, level); } - const ImageInfo& ImageInfoAt(TexImageTarget imageTarget, GLint level) const + const ImageInfo& ImageInfoAt(TexImageTarget texImageTarget, GLint level) const { + return const_cast(this)->ImageInfoAt(texImageTarget, level); + } + + void SetImageInfoAt(TexImageTarget texImageTarget, GLint level, + const ImageInfo& val) { - return const_cast(this)->ImageInfoAt(imageTarget, level); + ImageInfo* target = &ImageInfoAt(texImageTarget, level); + SetImageInfo(target, val); } - bool HasImageInfoAt(TexImageTarget imageTarget, GLint level) const { - size_t face = FaceForTarget(imageTarget); - CheckedUint32 checked_index = CheckedUint32(level) * mFacesCount + face; - return checked_index.isValid() && - checked_index.value() < mImageInfos.Length() && - ImageInfoAt(imageTarget, level).mImageDataStatus != WebGLImageDataStatus::NoImageData; - } + const ImageInfo& BaseImageInfo() const { + if (mBaseMipmapLevel >= kMaxLevelCount) + return ImageInfo::kUndefined; - ImageInfo& ImageInfoBase() { - return ImageInfoAtFace(0, 0); - } - - const ImageInfo& ImageInfoBase() const { - return ImageInfoAtFace(0, 0); + return ImageInfoAtFace(0, mBaseMipmapLevel); } size_t MemoryUsage() const; - void SetImageDataStatus(TexImageTarget imageTarget, GLint level, - WebGLImageDataStatus newStatus) - { - MOZ_ASSERT(HasImageInfoAt(imageTarget, level)); - ImageInfo& imageInfo = ImageInfoAt(imageTarget, level); - // There is no way to go from having image data to not having any. - MOZ_ASSERT(newStatus != WebGLImageDataStatus::NoImageData || - imageInfo.mImageDataStatus == WebGLImageDataStatus::NoImageData); - - if (imageInfo.mImageDataStatus != newStatus) - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - - imageInfo.mImageDataStatus = newStatus; - } - - bool EnsureInitializedImageData(TexImageTarget imageTarget, GLint level); - + bool InitializeImageData(const char* funcName, TexImageTarget target, uint32_t level); protected: - - void EnsureMaxLevelWithCustomImagesAtLeast(size_t maxLevelWithCustomImages) { - mMaxLevelWithCustomImages = std::max(mMaxLevelWithCustomImages, - maxLevelWithCustomImages); - mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount); - } + bool EnsureImageDataInitialized(const char* funcName, TexImageTarget target, + uint32_t level); bool CheckFloatTextureFilterParams() const { // Without OES_texture_float_linear, only NEAREST and @@ -405,33 +357,7 @@ protected: mWrapT == LOCAL_GL_CLAMP_TO_EDGE; } - bool DoesMipmapHaveAllLevelsConsistentlyDefined(TexImageTarget texImageTarget) const; - public: - void Bind(TexTarget texTarget); - - void SetImageInfo(TexImageTarget target, GLint level, GLsizei width, - GLsizei height, GLsizei depth, TexInternalFormat format, - WebGLImageDataStatus status); - - void SetMinFilter(TexMinFilter minFilter) { - mMinFilter = minFilter; - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - } - void SetMagFilter(TexMagFilter magFilter) { - mMagFilter = magFilter; - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - } - void SetWrapS(TexWrap wrapS) { - mWrapS = wrapS; - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - } - void SetWrapT(TexWrap wrapT) { - mWrapT = wrapT; - SetFakeBlackStatus(WebGLTextureFakeBlackStatus::Unknown); - } - TexMinFilter MinFilter() const { return mMinFilter; } - bool DoesMinFilterRequireMipmap() const { return !(mMinFilter == LOCAL_GL_NEAREST || mMinFilter == LOCAL_GL_LINEAR); @@ -441,51 +367,31 @@ public: void SetCustomMipmap(); - bool IsFirstImagePowerOfTwo() const { - return ImageInfoBase().IsPowerOfTwo(); - } - bool AreAllLevel0ImageInfosEqual() const; bool IsMipmapComplete() const; bool IsCubeComplete() const; + bool IsComplete(const char** const out_reason) const; + bool IsMipmapCubeComplete() const; - void SetFakeBlackStatus(WebGLTextureFakeBlackStatus x); + bool IsCubeMap() const { return (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP); } - bool IsImmutable() const { return mImmutable; } - void SetImmutable() { mImmutable = true; } + // Resolve cache optimizations +protected: + bool GetFakeBlackType(const char* funcName, uint32_t texUnit, + FakeBlackType* const out_fakeBlack); +public: + bool ResolveForDraw(const char* funcName, uint32_t texUnit, + FakeBlackType* const out_fakeBlack); - void SetBaseMipmapLevel(size_t level) { mBaseMipmapLevel = level; } - void SetMaxMipmapLevel(size_t level) { mMaxMipmapLevel = level; } - - // Clamping (from ES 3.0.4, section 3.8 - Texturing). When not immutable, - // the ranges must be guarded. - size_t EffectiveBaseMipmapLevel() const { - if (IsImmutable()) - return std::min(mBaseMipmapLevel, mMaxLevelWithCustomImages); - return mBaseMipmapLevel; - } - size_t EffectiveMaxMipmapLevel() const { - if (IsImmutable()) { - return mozilla::clamped(mMaxMipmapLevel, EffectiveBaseMipmapLevel(), - mMaxLevelWithCustomImages); - } - return std::min(mMaxMipmapLevel, mMaxLevelWithCustomImages); - } - bool IsMipmapRangeValid() const; - - size_t MaxLevelWithCustomImages() const { return mMaxLevelWithCustomImages; } - - // Returns the current fake-black-status, except if it was Unknown, - // in which case this function resolves it first, so it never returns Unknown. - WebGLTextureFakeBlackStatus ResolvedFakeBlackStatus(); + void InvalidateResolveCache() { mIsResolved = false; } }; inline TexImageTarget -TexImageTargetForTargetAndFace(TexTarget target, size_t face) +TexImageTargetForTargetAndFace(TexTarget target, uint8_t face) { switch (target.get()) { case LOCAL_GL_TEXTURE_2D: @@ -500,6 +406,23 @@ TexImageTargetForTargetAndFace(TexTarget target, size_t face) } } +already_AddRefed +ImageFromVideo(dom::HTMLVideoElement* elem); + +GLenum +DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLsizei width, GLsizei height, + GLsizei depth, const void* data); +GLenum +DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, const webgl::PackingInfo& pi, const void* data); +GLenum +DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum sizedUnpackFormat, + GLsizei dataSize, const void* data); + } // namespace mozilla #endif // WEBGL_TEXTURE_H_ diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp index 6a6b3f047c..2e1b90909c 100644 --- a/dom/canvas/WebGLTextureUpload.cpp +++ b/dom/canvas/WebGLTextureUpload.cpp @@ -11,9 +11,12 @@ #include "GLContext.h" #include "mozilla/dom/HTMLVideoElement.h" #include "mozilla/dom/ImageData.h" +#include "mozilla/gfx/SourceSurfaceRawData.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Scoped.h" +#include "mozilla/unused.h" #include "ScopedGLHelpers.h" +#include "TexUnpackBlob.h" #include "WebGLContext.h" #include "WebGLContextUtils.h" #include "WebGLFramebuffer.h" @@ -21,11 +24,626 @@ namespace mozilla { -bool -DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t funcDims, - const char* funcName) +/* This file handles: + * TexStorage2D(texTarget, levels, internalFormat, width, height) + * TexStorage3D(texTarget, levels, intenralFormat, width, height, depth) + * + * TexImage2D(texImageTarget, level, internalFormat, width, height, border, unpackFormat, + * unpackType, data) + * TexImage3D(texImageTarget, level, internalFormat, width, height, depth, border, + * unpackFormat, unpackType, data) + * TexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, unpackFormat, + * unpackType, data) + * TexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, height, depth, + * unpackFormat, unpackType, data) + * + * CompressedTexImage2D(texImageTarget, level, internalFormat, width, height, border, + * imageSize, data) + * CompressedTexImage3D(texImageTarget, level, internalFormat, width, height, depth, + * border, imageSize, data) + * CompressedTexSubImage2D(texImageTarget, level, xOffset, yOffset, width, height, + * sizedUnpackFormat, imageSize, data) + * CompressedTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, width, + * height, depth, sizedUnpackFormat, imageSize, data) + * + * CopyTexImage2D(texImageTarget, level, internalFormat, x, y, width, height, border) + * CopyTexImage3D - "Because the framebuffer is inhererntly two-dimensional, there is no + * CopyTexImage3D command." + * CopyTexSubImage2D(texImageTarget, level, xOffset, yOffset, x, y, width, height) + * CopyTexSubImage3D(texImageTarget, level, xOffset, yOffset, zOffset, x, y, width, + * height) + */ + +//////////////////////////////////////// +// ArrayBufferView? + +static inline bool +DoesJSTypeMatchUnpackType(GLenum unpackType, js::Scalar::Type jsType) +{ + switch (unpackType) { + case LOCAL_GL_BYTE: + return jsType == js::Scalar::Type::Int8; + + case LOCAL_GL_UNSIGNED_BYTE: + return jsType == js::Scalar::Type::Uint8 || + jsType == js::Scalar::Type::Uint8Clamped; + + case LOCAL_GL_SHORT: + return jsType == js::Scalar::Type::Int16; + + case LOCAL_GL_UNSIGNED_SHORT: + case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: + case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: + case LOCAL_GL_UNSIGNED_SHORT_5_6_5: + case LOCAL_GL_HALF_FLOAT: + case LOCAL_GL_HALF_FLOAT_OES: + return jsType == js::Scalar::Type::Uint16; + + case LOCAL_GL_INT: + return jsType == js::Scalar::Type::Int32; + + case LOCAL_GL_UNSIGNED_INT: + case LOCAL_GL_UNSIGNED_INT_2_10_10_10_REV: + case LOCAL_GL_UNSIGNED_INT_10F_11F_11F_REV: + case LOCAL_GL_UNSIGNED_INT_5_9_9_9_REV: + case LOCAL_GL_UNSIGNED_INT_24_8: + return jsType == js::Scalar::Type::Uint32; + + case LOCAL_GL_FLOAT: + return jsType == js::Scalar::Type::Float32; + + default: + return false; + } +} + +static bool +ValidateUnpackArrayType(WebGLContext* webgl, const char* funcName, GLenum unpackType, + js::Scalar::Type jsType) +{ + if (DoesJSTypeMatchUnpackType(unpackType, jsType)) + return true; + + const auto& fua = webgl->mFormatUsage; + const GLenum fakeUnpackFormat = LOCAL_GL_RGBA; + if (!fua->AreUnpackEnumsValid(fakeUnpackFormat, unpackType)) { + webgl->ErrorInvalidEnum("%s: Invalid unpack type: 0x%04x", funcName, unpackType); + return false; + } + + webgl->ErrorInvalidOperation("%s: `pixels` be compatible with unpack `type`.", + funcName); + return false; +} + +static UniquePtr +UnpackBlobFromMaybeView(WebGLContext* webgl, const char* funcName, GLsizei width, + GLsizei height, GLsizei depth, GLenum unpackType, + const dom::Nullable& maybeView) +{ + size_t dataSize; + const void* data; + if (maybeView.IsNull()) { + dataSize = 0; + data = nullptr; + } else { + const auto& view = maybeView.Value(); + void* mutData; + js::Scalar::Type jsType; + ComputeLengthAndData(view, &mutData, &dataSize, &jsType); + data = mutData; + + if (!ValidateUnpackArrayType(webgl, funcName, unpackType, jsType)) + return nullptr; + } + + UniquePtr ret; + ret.reset(new webgl::TexUnpackBytes(width, height, depth, dataSize, data)); + return Move(ret); +} + +void +WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, GLint border, GLenum unpackFormat, + GLenum unpackType, + const dom::Nullable& maybeView) +{ + UniquePtr blob; + blob = UnpackBlobFromMaybeView(mContext, funcName, width, height, depth, unpackType, + maybeView); + if (!blob) + return; + + TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset, + zOffset, border, unpackFormat, unpackType, blob.get()); +} + +//////////////////////////////////////// +// ImageData + +static UniquePtr +UnpackBlobFromImageData(WebGLContext* webgl, const char* funcName, GLenum unpackType, + dom::ImageData* imageData, dom::Uint8ClampedArray* scopedArr) +{ + if (!imageData) { + // Spec says to generate an INVALID_VALUE error + webgl->ErrorInvalidValue("%s: null ImageData", funcName); + return nullptr; + } + + DebugOnly inited = scopedArr->Init(imageData->GetDataObject()); + MOZ_ASSERT(inited); + + scopedArr->ComputeLengthAndData(); + const DebugOnly dataSize = scopedArr->Length(); + const void* const data = scopedArr->Data(); + + const gfx::IntSize size(imageData->Width(), imageData->Height()); + const size_t stride = size.width * 4; + const gfx::SurfaceFormat surfFormat = gfx::SurfaceFormat::R8G8B8A8; + const bool ownsData = false; + + MOZ_ASSERT(dataSize == stride * size.height); + + const RefPtr surf = new gfx::SourceSurfaceRawData; + + uint8_t* wrappableData = (uint8_t*)data; + surf->InitWrappingData(wrappableData, size, stride, surfFormat, ownsData); + + // WhatWG "HTML Living Standard" (30 October 2015): + // "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned as + // non-premultiplied alpha values." + const bool surfIsAlphaPremult = false; + + UniquePtr ret; + ret.reset(new webgl::TexUnpackSurface(surf, surfIsAlphaPremult)); + return Move(ret); +} + +void +WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum unpackFormat, + GLenum unpackType, dom::ImageData* imageData) +{ + dom::Uint8ClampedArray scopedArr; + + UniquePtr blob; + blob = UnpackBlobFromImageData(mContext, funcName, unpackType, imageData, &scopedArr); + if (!blob) + return; + + const GLint border = 0; + TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset, + zOffset, border, unpackFormat, unpackType, blob.get()); +} + +//////////////////////////////////////// +// dom::Element + +void +WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, + GLint yOffset, GLint zOffset, GLenum unpackFormat, + GLenum unpackType, dom::Element* elem, + ErrorResult* const out_error) +{ + auto sfer = mContext->SurfaceFromElement(elem); + + // While it's counter-intuitive, the shape of the SFEResult API means that we should + // try to pull out a surface first, and then, if we do pull out a surface, check + // CORS/write-only/etc.. + UniquePtr blob; + const bool isAlphaPremult = sfer.mIsPremultiplied; + + const auto& layersImage = sfer.mLayersImage; + if (layersImage && !gfxPrefs::WebGLDisableDOMBlitUploads()) { + blob.reset(new webgl::TexUnpackImage(layersImage, isAlphaPremult)); + } else if (sfer.GetSourceSurface()) { + blob.reset(new webgl::TexUnpackSurface(sfer.GetSourceSurface(), isAlphaPremult)); + } + + if (blob) { + if (!sfer.mCORSUsed) { + auto& srcPrincipal = sfer.mPrincipal; + nsIPrincipal* dstPrincipal = mContext->GetCanvas()->NodePrincipal(); + + if (!dstPrincipal->Subsumes(srcPrincipal)) { + mContext->GenerateWarning("%s: Cross-origin elements require CORS.", + funcName); + out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + } + + if (sfer.mIsWriteOnly) { + // mIsWriteOnly defaults to true, and so will be true even if SFE merely + // failed. Thus we must test mIsWriteOnly after successfully retrieving an + // Image or SourceSurface. + mContext->GenerateWarning("%s: Element is write-only, thus cannot be" + " uploaded.", + funcName); + out_error->Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + } else { + mContext->GenerateWarning("%s: Failed to get data from DOM element. Implicit" + " width and height for this upload will be zero.", + funcName); + + const uint32_t width = 0; + const uint32_t height = 0; + const uint32_t depth = 1; // Implicit depth for DOM uploads is always 1. + const size_t byteCount = 0; + blob.reset(new webgl::TexUnpackBytes(width, height, depth, byteCount, nullptr)); + } + MOZ_ASSERT(blob); + + const GLint border = 0; + TexOrSubImage(isSubImage, funcName, target, level, internalFormat, xOffset, yOffset, + zOffset, border, unpackFormat, unpackType, blob.get()); +} + + +////////////////////////////////////////////////////////////////////////////////////////// + +void +WebGLTexture::TexOrSubImage(bool isSubImage, const char* funcName, TexImageTarget target, + GLint level, GLenum internalFormat, GLint xOffset, + GLint yOffset, GLint zOffset, GLint border, + GLenum unpackFormat, GLenum unpackType, + webgl::TexUnpackBlob* blob) +{ + if (isSubImage) { + TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, unpackFormat, + unpackType, blob); + } else { + TexImage(funcName, target, level, internalFormat, border, unpackFormat, + unpackType, blob); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// + +static bool +ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcName, + TexImageTarget target, GLint level, + WebGLTexture::ImageInfo** const out_imageInfo) +{ + // Check level + if (level < 0) { + webgl->ErrorInvalidValue("%s: `level` must be >= 0.", funcName); + return false; + } + + if (level >= WebGLTexture::kMaxLevelCount) { + webgl->ErrorInvalidValue("%s: `level` is too large.", funcName); + return false; + } + + WebGLTexture::ImageInfo& imageInfo = texture->ImageInfoAt(target, level); + + *out_imageInfo = &imageInfo; + return true; +} + + +// For *TexImage* +bool +WebGLTexture::ValidateTexImageSpecification(const char* funcName, TexImageTarget target, + GLint level, GLsizei width, GLsizei height, + GLsizei depth, GLint border, + WebGLTexture::ImageInfo** const out_imageInfo) +{ + if (mImmutable) { + mContext->ErrorInvalidOperation("%s: Specified texture is immutable.", funcName); + return false; + } + + // Do this early to validate `level`. + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo)) + return false; + + // Check border + if (border != 0) { + mContext->ErrorInvalidValue("%s: `border` must be 0.", funcName); + return false; + } + + if (width < 0 || height < 0 || depth < 0) { + /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification + * "If wt and ht are the specified image width and height, + * and if either wt or ht are less than zero, then the error + * INVALID_VALUE is generated." + */ + mContext->ErrorInvalidValue("%s: `width`/`height`/`depth` must be >= 0.", + funcName); + return false; + } + + /* GLES 3.0.4, p133-134: + * GL_MAX_TEXTURE_SIZE is *not* the max allowed texture size. Rather, it is the + * max (width/height) size guaranteed not to generate an INVALID_VALUE for too-large + * dimensions. Sizes larger than GL_MAX_TEXTURE_SIZE *may or may not* result in an + * INVALID_VALUE, or possibly GL_OOM. + * + * However, we have needed to set our maximums lower in the past to prevent resource + * corruption. Therefore we have mImplMaxTextureSize, which is neither necessarily + * lower nor higher than MAX_TEXTURE_SIZE. + * + * Note that mImplMaxTextureSize must be >= than the advertized MAX_TEXTURE_SIZE. + * For simplicity, we advertize MAX_TEXTURE_SIZE as mImplMaxTextureSize. + */ + + uint32_t maxWidthHeight = 0; + uint32_t maxDepth = 0; + + MOZ_ASSERT(level <= 31); + switch (target.get()) { + case LOCAL_GL_TEXTURE_2D: + maxWidthHeight = mContext->mImplMaxTextureSize >> level; + maxDepth = 1; + break; + + case LOCAL_GL_TEXTURE_3D: + maxWidthHeight = mContext->mImplMax3DTextureSize >> level; + maxDepth = maxWidthHeight; + break; + + case LOCAL_GL_TEXTURE_2D_ARRAY: + maxWidthHeight = mContext->mImplMaxTextureSize >> level; + // "The maximum number of layers for two-dimensional array textures (depth) + // must be at least MAX_ARRAY_TEXTURE_LAYERS for all levels." + maxDepth = mContext->mImplMaxArrayTextureLayers; + break; + + default: // cube maps + MOZ_ASSERT(IsCubeMap()); + maxWidthHeight = mContext->mImplMaxCubeMapTextureSize >> level; + maxDepth = 1; + break; + } + + if (uint32_t(width) > maxWidthHeight || + uint32_t(height) > maxWidthHeight || + uint32_t(depth) > maxDepth) + { + mContext->ErrorInvalidValue("%s: Requested size at this level is unsupported.", + funcName); + return false; + } + + { + /* GL ES Version 2.0.25 - 3.7.1 Texture Image Specification + * "If level is greater than zero, and either width or + * height is not a power-of-two, the error INVALID_VALUE is + * generated." + * + * This restriction does not apply to GL ES Version 3.0+. + */ + bool requirePOT = (!mContext->IsWebGL2() && level != 0); + + if (requirePOT) { + if (!IsPowerOfTwo(uint32_t(width)) || !IsPowerOfTwo(uint32_t(height))) { + mContext->ErrorInvalidValue("%s: For level > 0, width and height must be" + " powers of two.", + funcName); + return false; + } + } + } + + *out_imageInfo = imageInfo; + return true; +} + +// For *TexSubImage* +bool +WebGLTexture::ValidateTexImageSelection(const char* funcName, TexImageTarget target, + GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, + WebGLTexture::ImageInfo** const out_imageInfo) +{ + // The conformance test wants bad arg checks before imageInfo checks. + if (xOffset < 0 || yOffset < 0 || zOffset < 0 || + width < 0 || height < 0 || depth < 0) + { + mContext->ErrorInvalidValue("%s: Offsets and dimensions must be >=0.", funcName); + return false; + } + + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImage(mContext, this, funcName, target, level, &imageInfo)) + return false; + + if (!imageInfo->IsDefined()) { + mContext->ErrorInvalidOperation("%s: The specified TexImage has not yet been" + " specified.", + funcName); + return false; + } + + const auto totalX = CheckedUint32(xOffset) + width; + const auto totalY = CheckedUint32(yOffset) + height; + const auto totalZ = CheckedUint32(zOffset) + depth; + + if (!totalX.isValid() || totalX.value() > imageInfo->mWidth || + !totalY.isValid() || totalY.value() > imageInfo->mHeight || + !totalZ.isValid() || totalZ.value() > imageInfo->mDepth) + { + mContext->ErrorInvalidValue("%s: Offset+size must be <= the size of the existing" + " specified image.", + funcName); + return false; + } + + *out_imageInfo = imageInfo; + return true; +} + +static bool +ValidateCompressedTexUnpack(WebGLContext* webgl, const char* funcName, GLsizei width, + GLsizei height, GLsizei depth, + const webgl::FormatInfo* format, size_t dataSize) +{ + auto compression = format->compression; + + auto bytesPerBlock = compression->bytesPerBlock; + auto blockWidth = compression->blockWidth; + auto blockHeight = compression->blockHeight; + + auto widthInBlocks = CheckedUint32(width) / blockWidth; + auto heightInBlocks = CheckedUint32(height) / blockHeight; + if (width % blockWidth) widthInBlocks += 1; + if (height % blockHeight) heightInBlocks += 1; + + const CheckedUint32 blocksPerImage = widthInBlocks * heightInBlocks; + const CheckedUint32 bytesPerImage = bytesPerBlock * blocksPerImage; + const CheckedUint32 bytesNeeded = bytesPerImage * depth; + + if (!bytesNeeded.isValid()) { + webgl->ErrorOutOfMemory("%s: Overflow while computing the needed buffer size.", + funcName); + return false; + } + + if (dataSize != bytesNeeded.value()) { + webgl->ErrorInvalidValue("%s: Provided buffer's size must match expected size." + " (needs %u, has %u)", + funcName, bytesNeeded.value(), dataSize); + return false; + } + + return true; +} + +static bool +DoChannelsMatchForCopyTexImage(const webgl::FormatInfo* srcFormat, + const webgl::FormatInfo* dstFormat) +{ + // GLES 3.0.4 p140 Table 3.16 "Valid CopyTexImage source framebuffer/destination + // texture base internal format combinations." + + switch (srcFormat->unsizedFormat) { + case webgl::UnsizedFormat::RGBA: + switch (dstFormat->unsizedFormat) { + case webgl::UnsizedFormat::A: + case webgl::UnsizedFormat::L: + case webgl::UnsizedFormat::LA: + case webgl::UnsizedFormat::R: + case webgl::UnsizedFormat::RG: + case webgl::UnsizedFormat::RGB: + case webgl::UnsizedFormat::RGBA: + return true; + default: + return false; + } + + case webgl::UnsizedFormat::RGB: + switch (dstFormat->unsizedFormat) { + case webgl::UnsizedFormat::L: + case webgl::UnsizedFormat::R: + case webgl::UnsizedFormat::RG: + case webgl::UnsizedFormat::RGB: + return true; + default: + return false; + } + + case webgl::UnsizedFormat::RG: + switch (dstFormat->unsizedFormat) { + case webgl::UnsizedFormat::L: + case webgl::UnsizedFormat::R: + case webgl::UnsizedFormat::RG: + return true; + default: + return false; + } + + case webgl::UnsizedFormat::R: + switch (dstFormat->unsizedFormat) { + case webgl::UnsizedFormat::L: + case webgl::UnsizedFormat::R: + return true; + default: + return false; + } + + default: + return false; + } +} + +static bool +EnsureImageDataInitializedForUpload(WebGLTexture* tex, const char* funcName, + TexImageTarget target, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, uint32_t width, + uint32_t height, uint32_t depth, + WebGLTexture::ImageInfo* imageInfo, + bool* const out_uploadWillInitialize) +{ + *out_uploadWillInitialize = false; + + if (!imageInfo->IsDataInitialized()) { + const bool isFullUpload = (!xOffset && !yOffset && !zOffset && + width == imageInfo->mWidth && + height == imageInfo->mHeight && + depth == imageInfo->mDepth); + if (isFullUpload) { + *out_uploadWillInitialize = true; + } else { + WebGLContext* webgl = tex->mContext; + webgl->GenerateWarning("%s: Texture has not been initialized prior to a" + " partial upload, forcing the browser to clear it." + " This may be slow.", + funcName); + if (!tex->InitializeImageData(funcName, target, level)) { + MOZ_ASSERT(false, "Unexpected failure to init image data."); + return false; + } + } + } + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +// Actual calls + +static inline GLenum +DoTexStorage(gl::GLContext* gl, TexTarget target, GLsizei levels, GLenum sizedFormat, + GLsizei width, GLsizei height, GLsizei depth) +{ + gl::GLContext::LocalErrorScope errorScope(*gl); + + switch (target.get()) { + case LOCAL_GL_TEXTURE_2D: + case LOCAL_GL_TEXTURE_CUBE_MAP: + MOZ_ASSERT(depth == 1); + gl->fTexStorage2D(target.get(), levels, sizedFormat, width, height); + break; + + case LOCAL_GL_TEXTURE_3D: + case LOCAL_GL_TEXTURE_2D_ARRAY: + gl->fTexStorage3D(target.get(), levels, sizedFormat, width, height, depth); + break; + + default: + MOZ_CRASH("bad target"); + } + + return errorScope.GetError(); +} + +static bool +Is3D(TexImageTarget target) { - uint8_t targetDims; switch (target.get()) { case LOCAL_GL_TEXTURE_2D: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: @@ -34,1384 +652,1264 @@ DoesTargetMatchDimensions(WebGLContext* webgl, TexImageTarget target, uint8_t fu case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: - targetDims = 2; - break; + return false; case LOCAL_GL_TEXTURE_3D: - targetDims = 3; - break; + case LOCAL_GL_TEXTURE_2D_ARRAY: + return true; default: - MOZ_CRASH("Unhandled texImageTarget."); - } - - if (targetDims != funcDims) { - webgl->ErrorInvalidEnum("%s: `target` must match function dimensions.", funcName); - return false; - } - - return true; -} - -void -WebGLTexture::CompressedTexImage2D(TexImageTarget texImageTarget, - GLint level, - GLenum internalFormat, - GLsizei width, GLsizei height, GLint border, - const dom::ArrayBufferViewOrSharedArrayBufferView& view) -{ - const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D; - - const char funcName[] = "compressedTexImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - if (!mContext->ValidateTexImage(texImageTarget.get(), level, internalFormat, - 0, 0, 0, width, height, 0, - border, LOCAL_GL_NONE, - LOCAL_GL_NONE, - func, dims)) - { - return; - } - - size_t byteLength; - void* data; - js::Scalar::Type dataType; - ComputeLengthAndData(view, &data, &byteLength, &dataType); - - if (!mContext->ValidateCompTexImageDataSize(level, internalFormat, width, height, byteLength, func, dims)) { - return; - } - - if (!mContext->ValidateCompTexImageSize(level, internalFormat, 0, 0, width, height, width, height, func, dims)) - { - return; - } - - if (mImmutable) { - return mContext->ErrorInvalidOperation( - "compressedTexImage2D: disallowed because the texture bound to " - "this target has already been made immutable by texStorage2D"); - } - - mContext->MakeContextCurrent(); - gl::GLContext* gl = mContext->gl; - gl->fCompressedTexImage2D(texImageTarget.get(), level, internalFormat, width, height, border, byteLength, data); - - SetImageInfo(texImageTarget, level, width, height, 1, internalFormat, - WebGLImageDataStatus::InitializedImageData); -} - -void -WebGLTexture::CompressedTexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLsizei width, GLsizei height, - GLenum internalFormat, - const dom::ArrayBufferViewOrSharedArrayBufferView& view) -{ - const WebGLTexImageFunc func = WebGLTexImageFunc::CompTexSubImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D; - - const char funcName[] = "compressedTexSubImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - if (!mContext->ValidateTexImage(texImageTarget.get(), - level, internalFormat, - xOffset, yOffset, 0, - width, height, 0, - 0, LOCAL_GL_NONE, LOCAL_GL_NONE, - func, dims)) - { - return; - } - - WebGLTexture::ImageInfo& levelInfo = ImageInfoAt(texImageTarget, level); - - if (internalFormat != levelInfo.EffectiveInternalFormat()) { - return mContext->ErrorInvalidOperation("compressedTexImage2D: internalFormat does not match the existing image"); - } - - size_t byteLength; - void* data; - js::Scalar::Type dataType; - ComputeLengthAndData(view, &data, &byteLength, &dataType); - - if (!mContext->ValidateCompTexImageDataSize(level, internalFormat, width, height, byteLength, func, dims)) - return; - - if (!mContext->ValidateCompTexImageSize(level, internalFormat, - xOffset, yOffset, - width, height, - levelInfo.Width(), levelInfo.Height(), - func, dims)) - { - return; - } - - if (levelInfo.HasUninitializedImageData()) { - bool coversWholeImage = xOffset == 0 && - yOffset == 0 && - width == levelInfo.Width() && - height == levelInfo.Height(); - if (coversWholeImage) { - SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData); - } else { - if (!EnsureInitializedImageData(texImageTarget, level)) - return; - } - } - - mContext->MakeContextCurrent(); - gl::GLContext* gl = mContext->gl; - gl->fCompressedTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, width, height, internalFormat, byteLength, data); -} - -void -WebGLTexture::CopyTexSubImage2D_base(TexImageTarget texImageTarget, GLint level, - TexInternalFormat internalFormat, - GLint xOffset, GLint yOffset, GLint x, - GLint y, GLsizei width, GLsizei height, - bool sub) -{ - const WebGLRectangleObject* framebufferRect = mContext->CurValidReadFBRectObject(); - GLsizei framebufferWidth = framebufferRect ? framebufferRect->Width() : 0; - GLsizei framebufferHeight = framebufferRect ? framebufferRect->Height() : 0; - - WebGLTexImageFunc func = sub - ? WebGLTexImageFunc::CopyTexSubImage - : WebGLTexImageFunc::CopyTexImage; - WebGLTexDimensions dims = WebGLTexDimensions::Tex2D; - const char* info = InfoFrom(func, dims); - - // TODO: This changes with color_buffer_float. Reassess when the - // patch lands. - if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat.get(), - xOffset, yOffset, 0, - width, height, 0, - 0, - LOCAL_GL_NONE, LOCAL_GL_NONE, - func, dims)) - { - return; - } - - if (!mContext->ValidateCopyTexImage(internalFormat.get(), func, dims)) - return; - - if (!mContext->mBoundReadFramebuffer) - mContext->ClearBackbufferIfNeeded(); - - mContext->MakeContextCurrent(); - gl::GLContext* gl = mContext->gl; - - if (mImmutable) { - if (!sub) { - return mContext->ErrorInvalidOperation("copyTexImage2D: disallowed because the texture bound to this target has already been made immutable by texStorage2D"); - } - } - - TexType framebuffertype = LOCAL_GL_NONE; - if (mContext->mBoundReadFramebuffer) { - TexInternalFormat framebuffereffectiveformat = mContext->mBoundReadFramebuffer->ColorAttachment(0).EffectiveInternalFormat(); - framebuffertype = TypeFromInternalFormat(framebuffereffectiveformat); - } else { - // FIXME - here we're assuming that the default framebuffer is backed by UNSIGNED_BYTE - // that might not always be true, say if we had a 16bpp default framebuffer. - framebuffertype = LOCAL_GL_UNSIGNED_BYTE; - } - - TexInternalFormat effectiveInternalFormat = - EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalFormat, framebuffertype); - - // this should never fail, validation happened earlier. - MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE); - - const bool widthOrHeightIsZero = (width == 0 || height == 0); - if (gl->WorkAroundDriverBugs() && - sub && widthOrHeightIsZero) - { - // NV driver on Linux complains that CopyTexSubImage2D(level=0, - // xOffset=0, yOffset=2, x=0, y=0, width=0, height=0) from a 300x150 FB - // to a 0x2 texture. This a useless thing to do, but technically legal. - // NV331.38 generates INVALID_VALUE. - return mContext->DummyFramebufferOperation(info); - } - - // check if the memory size of this texture may change with this call - bool sizeMayChange = !sub; - if (!sub && HasImageInfoAt(texImageTarget, level)) { - const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level); - sizeMayChange = width != imageInfo.Width() || - height != imageInfo.Height() || - effectiveInternalFormat != imageInfo.EffectiveInternalFormat(); - } - - if (sizeMayChange) - mContext->GetAndFlushUnderlyingGLErrors(); - - if (CanvasUtils::CheckSaneSubrectSize(x, y, width, height, framebufferWidth, framebufferHeight)) { - if (sub) - gl->fCopyTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, x, y, width, height); - else - gl->fCopyTexImage2D(texImageTarget.get(), level, internalFormat.get(), x, y, width, height, 0); - } else { - - // the rect doesn't fit in the framebuffer - - // first, we initialize the texture as black - if (!sub) { - SetImageInfo(texImageTarget, level, width, height, 1, - effectiveInternalFormat, - WebGLImageDataStatus::UninitializedImageData); - if (!EnsureInitializedImageData(texImageTarget, level)) - return; - } - - // if we are completely outside of the framebuffer, we can exit now with our black texture - if ( x >= framebufferWidth - || x+width <= 0 - || y >= framebufferHeight - || y+height <= 0) - { - // we are completely outside of range, can exit now with buffer filled with zeros - return mContext->DummyFramebufferOperation(info); - } - - GLint actual_x = clamped(x, 0, framebufferWidth); - GLint actual_x_plus_width = clamped(x + width, 0, framebufferWidth); - GLsizei actual_width = actual_x_plus_width - actual_x; - GLint actual_xOffset = xOffset + actual_x - x; - - GLint actual_y = clamped(y, 0, framebufferHeight); - GLint actual_y_plus_height = clamped(y + height, 0, framebufferHeight); - GLsizei actual_height = actual_y_plus_height - actual_y; - GLint actual_yOffset = yOffset + actual_y - y; - - gl->fCopyTexSubImage2D(texImageTarget.get(), level, actual_xOffset, actual_yOffset, actual_x, actual_y, actual_width, actual_height); - } - - if (sizeMayChange) { - GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); - if (error) { - mContext->GenerateWarning("copyTexImage2D generated error %s", mContext->ErrorName(error)); - return; - } - } - - if (!sub) { - SetImageInfo(texImageTarget, level, width, height, 1, - effectiveInternalFormat, - WebGLImageDataStatus::InitializedImageData); + MOZ_CRASH("bad target"); } } -void -WebGLTexture::CopyTexImage2D(TexImageTarget texImageTarget, - GLint level, - GLenum internalFormat, - GLint x, - GLint y, - GLsizei width, - GLsizei height, - GLint border) -{ - // copyTexImage2D only generates textures with type = UNSIGNED_BYTE - const WebGLTexImageFunc func = WebGLTexImageFunc::CopyTexImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D; - - const char funcName[] = "copyTexImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - if (!mContext->ValidateTexImage(texImageTarget.get(), level, internalFormat, - 0, 0, 0, - width, height, 0, - border, LOCAL_GL_NONE, LOCAL_GL_NONE, - func, dims)) - { - return; - } - - if (!mContext->ValidateCopyTexImage(internalFormat, func, dims)) - return; - - if (!mContext->mBoundReadFramebuffer) - mContext->ClearBackbufferIfNeeded(); - - CopyTexSubImage2D_base(texImageTarget, level, internalFormat, 0, 0, x, y, width, height, false); -} - -void -WebGLTexture::CopyTexSubImage2D(TexImageTarget texImageTarget, - GLint level, - GLint xOffset, - GLint yOffset, - GLint x, - GLint y, - GLsizei width, - GLsizei height) -{ - switch (texImageTarget.get()) { - case LOCAL_GL_TEXTURE_2D: - case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X: - case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X: - case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y: - case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: - case LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z: - case LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: - break; - default: - return mContext->ErrorInvalidEnumInfo("copyTexSubImage2D: target", texImageTarget.get()); - } - - if (level < 0) - return mContext->ErrorInvalidValue("copyTexSubImage2D: level may not be negative"); - - GLsizei maxTextureSize = mContext->MaxTextureSizeForTarget(TexImageTargetToTexTarget(texImageTarget)); - if (!(maxTextureSize >> level)) - return mContext->ErrorInvalidValue("copyTexSubImage2D: 2^level exceeds maximum texture size"); - - if (width < 0 || height < 0) - return mContext->ErrorInvalidValue("copyTexSubImage2D: width and height may not be negative"); - - if (xOffset < 0 || yOffset < 0) - return mContext->ErrorInvalidValue("copyTexSubImage2D: xOffset and yOffset may not be negative"); - - if (!HasImageInfoAt(texImageTarget, level)) - return mContext->ErrorInvalidOperation("copyTexSubImage2D: no texture image previously defined for this level and face"); - - const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level); - GLsizei texWidth = imageInfo.Width(); - GLsizei texHeight = imageInfo.Height(); - - if (xOffset + width > texWidth || xOffset + width < 0) - return mContext->ErrorInvalidValue("copyTexSubImage2D: xOffset+width is too large"); - - if (yOffset + height > texHeight || yOffset + height < 0) - return mContext->ErrorInvalidValue("copyTexSubImage2D: yOffset+height is too large"); - - if (!mContext->mBoundReadFramebuffer) - mContext->ClearBackbufferIfNeeded(); - - if (imageInfo.HasUninitializedImageData()) { - bool coversWholeImage = xOffset == 0 && - yOffset == 0 && - width == texWidth && - height == texHeight; - if (coversWholeImage) { - SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData); - } else { - if (!EnsureInitializedImageData(texImageTarget, level)) - return; - } - } - - TexInternalFormat internalFormat; - TexType type; - UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(imageInfo.EffectiveInternalFormat(), - &internalFormat, &type); - return CopyTexSubImage2D_base(texImageTarget, level, internalFormat, xOffset, yOffset, x, y, width, height, true); -} - - GLenum -WebGLTexture::CheckedTexImage2D(TexImageTarget texImageTarget, - GLint level, - TexInternalFormat internalFormat, - GLsizei width, - GLsizei height, - GLint border, - TexFormat unpackFormat, - TexType unpackType, - const GLvoid* data) +DoTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, + const webgl::DriverUnpackInfo* dui, GLsizei width, GLsizei height, + GLsizei depth, const void* data) { - TexInternalFormat effectiveInternalFormat = - EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType); - bool sizeMayChange = true; + const GLint border = 0; - if (HasImageInfoAt(texImageTarget, level)) { - const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level); - sizeMayChange = width != imageInfo.Width() || - height != imageInfo.Height() || - effectiveInternalFormat != imageInfo.EffectiveInternalFormat(); - } + gl::GLContext::LocalErrorScope errorScope(*gl); - gl::GLContext* gl = mContext->gl; - - // Convert to format and type required by OpenGL 'driver'. - GLenum driverType = LOCAL_GL_NONE; - GLenum driverInternalFormat = LOCAL_GL_NONE; - GLenum driverFormat = LOCAL_GL_NONE; - DriverFormatsFromEffectiveInternalFormat(gl, - effectiveInternalFormat, - &driverInternalFormat, - &driverFormat, - &driverType); - - if (sizeMayChange) { - mContext->GetAndFlushUnderlyingGLErrors(); - } - - gl->fTexImage2D(texImageTarget.get(), level, driverInternalFormat, width, height, border, driverFormat, driverType, data); - - if (effectiveInternalFormat != driverInternalFormat) - SetLegacyTextureSwizzle(gl, texImageTarget.get(), internalFormat.get()); - - GLenum error = LOCAL_GL_NO_ERROR; - if (sizeMayChange) { - error = mContext->GetAndFlushUnderlyingGLErrors(); - } - - return error; -} - -void -WebGLTexture::TexImage2D_base(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei srcStrideOrZero, - GLint border, - GLenum unpackFormat, - GLenum unpackType, - void* data, uint32_t byteLength, - js::Scalar::Type jsArrayType, - WebGLTexelFormat srcFormat, bool srcPremultiplied) -{ - const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D; - - if (unpackType == LOCAL_GL_HALF_FLOAT_OES) { - unpackType = LOCAL_GL_HALF_FLOAT; - } - - if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat, - 0, 0, 0, - width, height, 0, - border, unpackFormat, unpackType, func, dims)) - { - return; - } - - const bool isDepthTexture = unpackFormat == LOCAL_GL_DEPTH_COMPONENT || - unpackFormat == LOCAL_GL_DEPTH_STENCIL; - - if (isDepthTexture && !mContext->IsWebGL2()) { - if (data != nullptr || level != 0) - return mContext->ErrorInvalidOperation("texImage2D: " - "with format of DEPTH_COMPONENT or DEPTH_STENCIL, " - "data must be nullptr, " - "level must be zero"); - } - - if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims)) - return; - - TexInternalFormat effectiveInternalFormat = - EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType); - - if (effectiveInternalFormat == LOCAL_GL_NONE) { - return mContext->ErrorInvalidOperation("texImage2D: bad combination of internalFormat and type"); - } - - size_t srcTexelSize = size_t(-1); - if (srcFormat == WebGLTexelFormat::Auto) { - // we need to find the exact sized format of the source data. Slightly abusing - // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format - // is the same thing as an unsized internalFormat. - TexInternalFormat effectiveSourceFormat = - EffectiveInternalFormatFromInternalFormatAndType(unpackFormat, unpackType); - MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated format/type combo earlier - const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat); - MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here. - srcTexelSize = srcbitsPerTexel / 8; + if (Is3D(target)) { + gl->fTexImage3D(target.get(), level, dui->internalFormat, width, height, depth, + border, dui->unpackFormat, dui->unpackType, data); } else { - srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat); + MOZ_ASSERT(depth == 1); + gl->fTexImage2D(target.get(), level, dui->internalFormat, width, height, border, + dui->unpackFormat, dui->unpackType, data); } - CheckedUint32 checked_neededByteLength = - mContext->GetImageSize(height, width, 1, srcTexelSize, mContext->mPixelStoreUnpackAlignment); - - CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize; - CheckedUint32 checked_alignedRowSize = - RoundedToNextMultipleOf(checked_plainRowSize.value(), mContext->mPixelStoreUnpackAlignment); - - if (!checked_neededByteLength.isValid()) - return mContext->ErrorInvalidOperation("texImage2D: integer overflow computing the needed buffer size"); - - uint32_t bytesNeeded = checked_neededByteLength.value(); - - if (byteLength && byteLength < bytesNeeded) - return mContext->ErrorInvalidOperation("texImage2D: not enough data for operation (need %d, have %d)", - bytesNeeded, byteLength); - - if (mImmutable) { - return mContext->ErrorInvalidOperation( - "texImage2D: disallowed because the texture " - "bound to this target has already been made immutable by texStorage2D"); - } - mContext->MakeContextCurrent(); - - nsAutoArrayPtr convertedData; - void* pixels = nullptr; - WebGLImageDataStatus imageInfoStatusIfSuccess = WebGLImageDataStatus::UninitializedImageData; - - WebGLTexelFormat dstFormat = GetWebGLTexelFormat(effectiveInternalFormat); - WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat; - - if (byteLength) { - size_t bitsPerTexel = GetBitsPerTexel(effectiveInternalFormat); - MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here. - size_t dstTexelSize = bitsPerTexel / 8; - size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value(); - size_t dstPlainRowSize = dstTexelSize * width; - size_t unpackAlignment = mContext->mPixelStoreUnpackAlignment; - size_t dstStride = ((dstPlainRowSize + unpackAlignment-1) / unpackAlignment) * unpackAlignment; - - if (actualSrcFormat == dstFormat && - srcPremultiplied == mContext->mPixelStorePremultiplyAlpha && - srcStride == dstStride && - !mContext->mPixelStoreFlipY) - { - // no conversion, no flipping, so we avoid copying anything and just pass the source pointer - pixels = data; - } - else - { - size_t convertedDataSize = height * dstStride; - convertedData = new (fallible) uint8_t[convertedDataSize]; - if (!convertedData) { - mContext->ErrorOutOfMemory("texImage2D: Ran out of memory when allocating" - " a buffer for doing format conversion."); - return; - } - if (!mContext->ConvertImage(width, height, srcStride, dstStride, - static_cast(data), convertedData, - actualSrcFormat, srcPremultiplied, - dstFormat, mContext->mPixelStorePremultiplyAlpha, dstTexelSize)) - { - return mContext->ErrorInvalidOperation("texImage2D: Unsupported texture format conversion"); - } - pixels = reinterpret_cast(convertedData.get()); - } - imageInfoStatusIfSuccess = WebGLImageDataStatus::InitializedImageData; - } - - GLenum error = CheckedTexImage2D(texImageTarget, level, internalFormat, width, - height, border, unpackFormat, unpackType, pixels); - - if (error) { - mContext->GenerateWarning("texImage2D generated error %s", mContext->ErrorName(error)); - return; - } - - // in all of the code paths above, we should have either initialized data, - // or allocated data and left it uninitialized, but in any case we shouldn't - // have NoImageData at this point. - MOZ_ASSERT(imageInfoStatusIfSuccess != WebGLImageDataStatus::NoImageData); - - SetImageInfo(texImageTarget, level, width, height, 1, - effectiveInternalFormat, imageInfoStatusIfSuccess); + return errorScope.GetError(); } -void -WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLsizei width, - GLsizei height, GLint border, GLenum unpackFormat, - GLenum unpackType, const dom::Nullable& maybeView, - ErrorResult* const out_rv) +GLenum +DoTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLsizei width, GLsizei height, GLsizei depth, + const webgl::PackingInfo& pi, const void* data) { - void* data; - size_t length; - js::Scalar::Type jsArrayType; - if (maybeView.IsNull()) { - data = nullptr; - length = 0; - jsArrayType = js::Scalar::MaxTypedArrayViewType; + gl::GLContext::LocalErrorScope errorScope(*gl); + + if (Is3D(target)) { + gl->fTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, width, height, + depth, pi.format, pi.type, data); } else { - const auto& view = maybeView.Value(); - ComputeLengthAndData(view, &data, &length, &jsArrayType); + MOZ_ASSERT(zOffset == 0); + MOZ_ASSERT(depth == 1); + gl->fTexSubImage2D(target.get(), level, xOffset, yOffset, width, height, + pi.format, pi.type, data); } - const char funcName[] = "texImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - return TexImage2D_base(texImageTarget, level, internalFormat, width, height, 0, border, unpackFormat, unpackType, - data, length, jsArrayType, - WebGLTexelFormat::Auto, false); + return errorScope.GetError(); } -void -WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLenum unpackFormat, - GLenum unpackType, dom::ImageData* imageData, ErrorResult* const out_rv) +static inline GLenum +DoCompressedTexImage(gl::GLContext* gl, TexImageTarget target, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, + GLint border, GLsizei dataSize, const void* data) { - if (!imageData) { - // Spec says to generate an INVALID_VALUE error - return mContext->ErrorInvalidValue("texImage2D: null ImageData"); - } + gl::GLContext::LocalErrorScope errorScope(*gl); - dom::Uint8ClampedArray arr; - DebugOnly inited = arr.Init(imageData->GetDataObject()); - MOZ_ASSERT(inited); - arr.ComputeLengthAndData(); - - void* pixelData = arr.Data(); - const uint32_t pixelDataLength = arr.Length(); - - const char funcName[] = "texImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - return TexImage2D_base(texImageTarget, level, internalFormat, imageData->Width(), - imageData->Height(), 4*imageData->Width(), 0, - unpackFormat, unpackType, pixelData, pixelDataLength, js::Scalar::MaxTypedArrayViewType, - WebGLTexelFormat::RGBA8, false); -} - -void -WebGLTexture::TexImage2D(TexImageTarget texImageTarget, GLint level, - GLenum internalFormat, GLenum unpackFormat, GLenum unpackType, - dom::Element* elem, ErrorResult* out_rv) -{ - const char funcName[] = "texImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - if (level < 0) - return mContext->ErrorInvalidValue("texImage2D: level is negative"); - - const int32_t maxLevel = mContext->MaxTextureLevelForTexImageTarget(texImageTarget); - if (level > maxLevel) { - mContext->ErrorInvalidValue("texImage2D: level %d is too large, max is %d", - level, maxLevel); - return; - } - - // Trying to handle the video by GPU directly first - if (TexImageFromVideoElement(texImageTarget, level, internalFormat, - unpackFormat, unpackType, elem)) - { - return; - } - - RefPtr data; - WebGLTexelFormat srcFormat; - nsLayoutUtils::SurfaceFromElementResult res = mContext->SurfaceFromElement(elem); - *out_rv = mContext->SurfaceFromElementResultToImageSurface(res, data, &srcFormat); - if (out_rv->Failed() || !data) - return; - - gfx::IntSize size = data->GetSize(); - uint32_t byteLength = data->Stride() * size.height; - return TexImage2D_base(texImageTarget, level, internalFormat, - size.width, size.height, data->Stride(), 0, - unpackFormat, unpackType, data->GetData(), byteLength, - js::Scalar::MaxTypedArrayViewType, srcFormat, - res.mIsPremultiplied); -} - - -void -WebGLTexture::TexSubImage2D_base(TexImageTarget texImageTarget, GLint level, - GLint xOffset, GLint yOffset, - GLsizei width, GLsizei height, GLsizei srcStrideOrZero, - GLenum unpackFormat, GLenum unpackType, - void* data, uint32_t byteLength, - js::Scalar::Type jsArrayType, - WebGLTexelFormat srcFormat, bool srcPremultiplied) -{ - const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex2D; - - if (unpackType == LOCAL_GL_HALF_FLOAT_OES) - unpackType = LOCAL_GL_HALF_FLOAT; - - const char funcName[] = "texSubImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - if (!HasImageInfoAt(texImageTarget, level)) - return mContext->ErrorInvalidOperation("texSubImage2D: no previously defined texture image"); - - const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level); - const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat(); - - if (!mContext->ValidateTexImage(texImageTarget, level, - existingEffectiveInternalFormat.get(), - xOffset, yOffset, 0, - width, height, 0, - 0, unpackFormat, unpackType, func, dims)) - { - return; - } - - if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims)) - return; - - if (unpackType != TypeFromInternalFormat(existingEffectiveInternalFormat)) { - return mContext->ErrorInvalidOperation("texSubImage2D: type differs from that of the existing image"); - } - - size_t srcTexelSize = size_t(-1); - if (srcFormat == WebGLTexelFormat::Auto) { - const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat); - MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here. - srcTexelSize = bitsPerTexel / 8; + if (Is3D(target)) { + gl->fCompressedTexImage3D(target.get(), level, internalFormat, width, height, + depth, border, dataSize, data); } else { - srcTexelSize = WebGLTexelConversions::TexelBytesForFormat(srcFormat); + MOZ_ASSERT(depth == 1); + gl->fCompressedTexImage2D(target.get(), level, internalFormat, width, height, + border, dataSize, data); } - if (width == 0 || height == 0) - return; // ES 2.0 says it has no effect, we better return right now - - CheckedUint32 checked_neededByteLength = - mContext->GetImageSize(height, width, 1, srcTexelSize, mContext->mPixelStoreUnpackAlignment); - - CheckedUint32 checked_plainRowSize = CheckedUint32(width) * srcTexelSize; - - CheckedUint32 checked_alignedRowSize = - RoundedToNextMultipleOf(checked_plainRowSize.value(), mContext->mPixelStoreUnpackAlignment); - - if (!checked_neededByteLength.isValid()) - return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); - - uint32_t bytesNeeded = checked_neededByteLength.value(); - - if (byteLength < bytesNeeded) - return mContext->ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength); - - if (imageInfo.HasUninitializedImageData()) { - bool coversWholeImage = xOffset == 0 && - yOffset == 0 && - width == imageInfo.Width() && - height == imageInfo.Height(); - if (coversWholeImage) { - SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData); - } else { - if (!EnsureInitializedImageData(texImageTarget, level)) - return; - } - } - mContext->MakeContextCurrent(); - gl::GLContext* gl = mContext->gl; - - size_t srcStride = srcStrideOrZero ? srcStrideOrZero : checked_alignedRowSize.value(); - uint32_t dstTexelSize = GetBitsPerTexel(existingEffectiveInternalFormat) / 8; - size_t dstPlainRowSize = dstTexelSize * width; - // There are checks above to ensure that this won't overflow. - size_t dstStride = RoundedToNextMultipleOf(dstPlainRowSize, mContext->mPixelStoreUnpackAlignment).value(); - - void* pixels = data; - nsAutoArrayPtr convertedData; - - WebGLTexelFormat dstFormat = GetWebGLTexelFormat(existingEffectiveInternalFormat); - WebGLTexelFormat actualSrcFormat = srcFormat == WebGLTexelFormat::Auto ? dstFormat : srcFormat; - - // no conversion, no flipping, so we avoid copying anything and just pass the source pointer - bool noConversion = (actualSrcFormat == dstFormat && - srcPremultiplied == mContext->mPixelStorePremultiplyAlpha && - srcStride == dstStride && - !mContext->mPixelStoreFlipY); - - if (!noConversion) { - size_t convertedDataSize = height * dstStride; - convertedData = new (fallible) uint8_t[convertedDataSize]; - if (!convertedData) { - mContext->ErrorOutOfMemory("texImage2D: Ran out of memory when allocating" - " a buffer for doing format conversion."); - return; - } - if (!mContext->ConvertImage(width, height, srcStride, dstStride, - static_cast(data), convertedData, - actualSrcFormat, srcPremultiplied, - dstFormat, mContext->mPixelStorePremultiplyAlpha, dstTexelSize)) - { - return mContext->ErrorInvalidOperation("texSubImage2D: Unsupported texture format conversion"); - } - pixels = reinterpret_cast(convertedData.get()); - } - - GLenum driverType = LOCAL_GL_NONE; - GLenum driverInternalFormat = LOCAL_GL_NONE; - GLenum driverFormat = LOCAL_GL_NONE; - DriverFormatsFromEffectiveInternalFormat(gl, - existingEffectiveInternalFormat, - &driverInternalFormat, - &driverFormat, - &driverType); - - gl->fTexSubImage2D(texImageTarget.get(), level, xOffset, yOffset, width, height, driverFormat, driverType, pixels); + return errorScope.GetError(); } -void -WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level, - GLint xOffset, GLint yOffset, - GLsizei width, GLsizei height, - GLenum unpackFormat, GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv) +GLenum +DoCompressedTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLsizei width, + GLsizei height, GLsizei depth, GLenum sizedUnpackFormat, + GLsizei dataSize, const void* data) { - if (maybeView.IsNull()) - return mContext->ErrorInvalidValue("texSubImage2D: pixels must not be null!"); + gl::GLContext::LocalErrorScope errorScope(*gl); - const auto& view = maybeView.Value(); - size_t length; - void* data; - js::Scalar::Type jsArrayType; - ComputeLengthAndData(view, &data, &length, &jsArrayType); + if (Is3D(target)) { + gl->fCompressedTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, + width, height, depth, sizedUnpackFormat, dataSize, + data); + } else { + MOZ_ASSERT(zOffset == 0); + MOZ_ASSERT(depth == 1); + gl->fCompressedTexSubImage2D(target.get(), level, xOffset, yOffset, width, + height, sizedUnpackFormat, dataSize, data); + } - const char funcName[] = "texSubImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; - - return TexSubImage2D_base(texImageTarget, level, xOffset, yOffset, - width, height, 0, unpackFormat, unpackType, - data, length, jsArrayType, - WebGLTexelFormat::Auto, false); + return errorScope.GetError(); } -void -WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level, - GLint xOffset, GLint yOffset, - GLenum unpackFormat, GLenum unpackType, dom::ImageData* imageData, - ErrorResult* const out_rv) +static inline GLenum +DoCopyTexImage2D(gl::GLContext* gl, TexImageTarget target, GLint level, + GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) { - if (!imageData) - return mContext->ErrorInvalidValue("texSubImage2D: pixels must not be null!"); + gl::GLContext::LocalErrorScope errorScope(*gl); - dom::Uint8ClampedArray arr; - DebugOnly inited = arr.Init(imageData->GetDataObject()); - MOZ_ASSERT(inited); - arr.ComputeLengthAndData(); + MOZ_ASSERT(!Is3D(target)); + gl->fCopyTexImage2D(target.get(), level, internalFormat, x, y, width, height, + border); - return TexSubImage2D_base(texImageTarget, level, xOffset, yOffset, - imageData->Width(), imageData->Height(), - 4*imageData->Width(), unpackFormat, unpackType, - arr.Data(), arr.Length(), - js::Scalar::MaxTypedArrayViewType, - WebGLTexelFormat::RGBA8, false); + return errorScope.GetError(); } - - -bool -WebGLTexture::TexImageFromVideoElement(TexImageTarget texImageTarget, - GLint level, GLenum internalFormat, - GLenum unpackFormat, GLenum unpackType, - mozilla::dom::Element* elem) +static inline GLenum +DoCopyTexSubImage(gl::GLContext* gl, TexImageTarget target, GLint level, GLint xOffset, + GLint yOffset, GLint zOffset, GLint x, GLint y, GLsizei width, + GLsizei height) { - if (unpackType == LOCAL_GL_HALF_FLOAT_OES && - !mContext->gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float)) - { - unpackType = LOCAL_GL_HALF_FLOAT; + gl::GLContext::LocalErrorScope errorScope(*gl); + + if (Is3D(target)) { + gl->fCopyTexSubImage3D(target.get(), level, xOffset, yOffset, zOffset, x, y, + width, height); + } else { + MOZ_ASSERT(zOffset == 0); + gl->fCopyTexSubImage2D(target.get(), level, xOffset, yOffset, x, y, width, + height); } - if (!mContext->ValidateTexImageFormatAndType(unpackFormat, unpackType, - WebGLTexImageFunc::TexImage, - WebGLTexDimensions::Tex2D)) - { - return false; - } + return errorScope.GetError(); +} - dom::HTMLVideoElement* video = dom::HTMLVideoElement::FromContentOrNull(elem); - if (!video) - return false; +////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////// +// Actual (mostly generic) function implementations - uint16_t readyState; - if (NS_SUCCEEDED(video->GetReadyState(&readyState)) && - readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) - { - //No frame inside, just return - return false; - } +static bool +ValidateCompressedTexImageRestrictions(const char* funcName, WebGLContext* webgl, + TexImageTarget target, uint32_t level, + const webgl::FormatInfo* format, uint32_t width, + uint32_t height, uint32_t depth) +{ + const auto fnIsDimValid_S3TC = [level](uint32_t size, uint32_t blockSize) { + if (size % blockSize == 0) + return true; - // If it doesn't have a principal, just bail - nsCOMPtr principal = video->GetCurrentPrincipal(); - if (!principal) - return false; + if (level == 0) + return false; - mozilla::layers::ImageContainer* container = video->GetImageContainer(); - if (!container) - return false; + return (size == 0 || size == 1 || size == 2); + }; - if (video->GetCORSMode() == CORS_NONE) { - bool subsumes; - nsresult rv = mContext->mCanvasElement->NodePrincipal()->Subsumes(principal, &subsumes); - if (NS_FAILED(rv) || !subsumes) { - mContext->GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. " - "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures"); + switch (format->compression->family) { + case webgl::CompressionFamily::PVRTC: + if (!IsPowerOfTwo(width) || !IsPowerOfTwo(height)) { + webgl->ErrorInvalidValue("%s: %s requires power-of-two width and height.", + funcName, format->name); return false; } + + break; + + case webgl::CompressionFamily::S3TC: + if (!fnIsDimValid_S3TC(width, format->compression->blockWidth) || + !fnIsDimValid_S3TC(height, format->compression->blockHeight)) + { + webgl->ErrorInvalidOperation("%s: %s requires that width and height are" + " block-aligned, or, if level>0, equal to 0, 1," + " or 2.", + funcName, format->name); + return false; + } + + break; + + // Default: There are no restrictions on CompressedTexImage. + default: // ATC, ETC1, ES3 + break; } - layers::AutoLockImage lockedImage(container); - layers::Image* srcImage = lockedImage.GetImage(); - if (!srcImage) { - return false; - } - - gl::GLContext* gl = mContext->gl; - gl->MakeCurrent(); - - const WebGLTexture::ImageInfo& info = ImageInfoAt(texImageTarget, 0); - bool dimensionsMatch = info.Width() == srcImage->GetSize().width && - info.Height() == srcImage->GetSize().height; - if (!dimensionsMatch) { - // we need to allocation - gl->fTexImage2D(texImageTarget.get(), level, internalFormat, - srcImage->GetSize().width, srcImage->GetSize().height, - 0, unpackFormat, unpackType, nullptr); - } - - const gl::OriginPos destOrigin = mContext->mPixelStoreFlipY ? gl::OriginPos::BottomLeft - : gl::OriginPos::TopLeft; - bool ok = gl->BlitHelper()->BlitImageToTexture(srcImage, - srcImage->GetSize(), - mGLName, - texImageTarget.get(), - destOrigin); - if (ok) { - TexInternalFormat effectiveInternalFormat = - EffectiveInternalFormatFromInternalFormatAndType(internalFormat, - unpackType); - MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE); - SetImageInfo(texImageTarget, level, srcImage->GetSize().width, - srcImage->GetSize().height, 1, - effectiveInternalFormat, - WebGLImageDataStatus::InitializedImageData); - Bind(TexImageTargetToTexTarget(texImageTarget)); - } - return ok; + return true; } -void -WebGLTexture::TexSubImage2D(TexImageTarget texImageTarget, GLint level, GLint xOffset, - GLint yOffset, GLenum unpackFormat, GLenum unpackType, - dom::Element* elem, ErrorResult* const out_rv) +static bool +ValidateTargetForFormat(const char* funcName, WebGLContext* webgl, TexImageTarget target, + const webgl::FormatInfo* format) { - const char funcName[] = "texSubImage2D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 2, funcName)) - return; + // GLES 3.0.4 p127: + // "Textures with a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL are + // supported by texture image specification commands only if `target` is TEXTURE_2D, + // TEXTURE_2D_ARRAY, or TEXTURE_CUBE_MAP. Using these formats in conjunction with any + // other `target` will result in an INVALID_OPERATION error." - if (level < 0) - return mContext->ErrorInvalidValue("texSubImage2D: level is negative"); + switch (format->effectiveFormat) { + // TEXTURE_2D_ARRAY but not TEXTURE_3D: + // D and DS formats + case webgl::EffectiveFormat::DEPTH_COMPONENT16: + case webgl::EffectiveFormat::DEPTH_COMPONENT24: + case webgl::EffectiveFormat::DEPTH_COMPONENT32F: + case webgl::EffectiveFormat::DEPTH24_STENCIL8: + case webgl::EffectiveFormat::DEPTH32F_STENCIL8: + // CompressionFamily::ES3 + case webgl::EffectiveFormat::COMPRESSED_R11_EAC: + case webgl::EffectiveFormat::COMPRESSED_SIGNED_R11_EAC: + case webgl::EffectiveFormat::COMPRESSED_RG11_EAC: + case webgl::EffectiveFormat::COMPRESSED_SIGNED_RG11_EAC: + case webgl::EffectiveFormat::COMPRESSED_RGB8_ETC2: + case webgl::EffectiveFormat::COMPRESSED_SRGB8_ETC2: + case webgl::EffectiveFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case webgl::EffectiveFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case webgl::EffectiveFormat::COMPRESSED_RGBA8_ETC2_EAC: + case webgl::EffectiveFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + // CompressionFamily::S3TC + case webgl::EffectiveFormat::COMPRESSED_RGB_S3TC_DXT1_EXT: + case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT1_EXT: + case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT3_EXT: + case webgl::EffectiveFormat::COMPRESSED_RGBA_S3TC_DXT5_EXT: + if (target == LOCAL_GL_TEXTURE_3D) { + webgl->ErrorInvalidOperation("%s: Format %s cannot be used with TEXTURE_3D.", + funcName, format->name); + return false; + } + break; - const int32_t maxLevel = mContext->MaxTextureLevelForTexImageTarget(texImageTarget); - if (level > maxLevel) { - mContext->ErrorInvalidValue("texSubImage2D: level %d is too large, max is %d", - level, maxLevel); - return; - } + // No 3D targets: + // CompressionFamily::ATC + case webgl::EffectiveFormat::ATC_RGB_AMD: + case webgl::EffectiveFormat::ATC_RGBA_EXPLICIT_ALPHA_AMD: + case webgl::EffectiveFormat::ATC_RGBA_INTERPOLATED_ALPHA_AMD: + // CompressionFamily::PVRTC + case webgl::EffectiveFormat::COMPRESSED_RGB_PVRTC_4BPPV1: + case webgl::EffectiveFormat::COMPRESSED_RGBA_PVRTC_4BPPV1: + case webgl::EffectiveFormat::COMPRESSED_RGB_PVRTC_2BPPV1: + case webgl::EffectiveFormat::COMPRESSED_RGBA_PVRTC_2BPPV1: + // CompressionFamily::ETC1 + case webgl::EffectiveFormat::ETC1_RGB8_OES: + if (target == LOCAL_GL_TEXTURE_3D || + target == LOCAL_GL_TEXTURE_2D_ARRAY) + { + webgl->ErrorInvalidOperation("%s: Format %s cannot be used with TEXTURE_3D or" + " TEXTURE_2D_ARRAY.", + funcName, format->name); + return false; + } + break; - const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level); - const TexInternalFormat internalFormat = imageInfo.EffectiveInternalFormat(); - - // Trying to handle the video by GPU directly first - if (TexImageFromVideoElement(texImageTarget, level, internalFormat.get(), unpackFormat, unpackType, elem)) - { - return; - } - - RefPtr data; - WebGLTexelFormat srcFormat; - nsLayoutUtils::SurfaceFromElementResult res = mContext->SurfaceFromElement(elem); - *out_rv = mContext->SurfaceFromElementResultToImageSurface(res, data, &srcFormat); - if (out_rv->Failed() || !data) - return; - - gfx::IntSize size = data->GetSize(); - uint32_t byteLength = data->Stride() * size.height; - TexSubImage2D_base(texImageTarget, level, xOffset, yOffset, size.width, - size.height, data->Stride(), unpackFormat, unpackType, data->GetData(), - byteLength, js::Scalar::MaxTypedArrayViewType, srcFormat, - res.mIsPremultiplied); -} - -bool -WebGLTexture::ValidateSizedInternalFormat(GLenum internalFormat, const char* info) -{ - switch (internalFormat) { - // Sized Internal Formats - // https://www.khronos.org/opengles/sdk/docs/man3/html/glTexStorage2D.xhtml - case LOCAL_GL_R8: - case LOCAL_GL_R8_SNORM: - case LOCAL_GL_R16F: - case LOCAL_GL_R32F: - case LOCAL_GL_R8UI: - case LOCAL_GL_R8I: - case LOCAL_GL_R16UI: - case LOCAL_GL_R16I: - case LOCAL_GL_R32UI: - case LOCAL_GL_R32I: - case LOCAL_GL_RG8: - case LOCAL_GL_RG8_SNORM: - case LOCAL_GL_RG16F: - case LOCAL_GL_RG32F: - case LOCAL_GL_RG8UI: - case LOCAL_GL_RG8I: - case LOCAL_GL_RG16UI: - case LOCAL_GL_RG16I: - case LOCAL_GL_RG32UI: - case LOCAL_GL_RG32I: - case LOCAL_GL_RGB8: - case LOCAL_GL_SRGB8: - case LOCAL_GL_RGB565: - case LOCAL_GL_RGB8_SNORM: - case LOCAL_GL_R11F_G11F_B10F: - case LOCAL_GL_RGB9_E5: - case LOCAL_GL_RGB16F: - case LOCAL_GL_RGB32F: - case LOCAL_GL_RGB8UI: - case LOCAL_GL_RGB8I: - case LOCAL_GL_RGB16UI: - case LOCAL_GL_RGB16I: - case LOCAL_GL_RGB32UI: - case LOCAL_GL_RGB32I: - case LOCAL_GL_RGBA8: - case LOCAL_GL_SRGB8_ALPHA8: - case LOCAL_GL_RGBA8_SNORM: - case LOCAL_GL_RGB5_A1: - case LOCAL_GL_RGBA4: - case LOCAL_GL_RGB10_A2: - case LOCAL_GL_RGBA16F: - case LOCAL_GL_RGBA32F: - case LOCAL_GL_RGBA8UI: - case LOCAL_GL_RGBA8I: - case LOCAL_GL_RGB10_A2UI: - case LOCAL_GL_RGBA16UI: - case LOCAL_GL_RGBA16I: - case LOCAL_GL_RGBA32I: - case LOCAL_GL_RGBA32UI: - case LOCAL_GL_DEPTH_COMPONENT16: - case LOCAL_GL_DEPTH_COMPONENT24: - case LOCAL_GL_DEPTH_COMPONENT32F: - case LOCAL_GL_DEPTH24_STENCIL8: - case LOCAL_GL_DEPTH32F_STENCIL8: - return true; - } - - if (IsCompressedTextureFormat(internalFormat)) - return true; - - nsCString name; - mContext->EnumName(internalFormat, &name); - mContext->ErrorInvalidEnum("%s: invalid internal format %s", info, name.get()); - - return false; -} - - -/** Validates parameters to texStorage{2D,3D} */ -bool -WebGLTexture::ValidateTexStorage(TexTarget texTarget, GLsizei levels, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, - const char* info) -{ - // GL_INVALID_OPERATION is generated if the texture object currently bound to target already has - // GL_TEXTURE_IMMUTABLE_FORMAT set to GL_TRUE. - if (mImmutable) { - mContext->ErrorInvalidOperation("%s: texture bound to target %s is already immutable", - info, mContext->EnumName(texTarget.get())); - return false; - } - - // GL_INVALID_ENUM is generated if internalFormat is not a valid sized internal format. - if (!ValidateSizedInternalFormat(internalFormat, info)) - return false; - - // GL_INVALID_VALUE is generated if width, height or levels are less than 1. - if (width < 1) { mContext->ErrorInvalidValue("%s: width is < 1", info); return false; } - if (height < 1) { mContext->ErrorInvalidValue("%s: height is < 1", info); return false; } - if (depth < 1) { mContext->ErrorInvalidValue("%s: depth is < 1", info); return false; } - if (levels < 1) { mContext->ErrorInvalidValue("%s: levels is < 1", info); return false; } - - // GL_INVALID_OPERATION is generated if levels is greater than floor(log2(max(width, height, depth)))+1. - if (FloorLog2(std::max(std::max(width, height), depth)) + 1 < levels) { - mContext->ErrorInvalidOperation("%s: too many levels for given texture dimensions", info); - return false; + default: + break; } return true; } void -WebGLTexture::TexStorage2D(TexTarget texTarget, GLsizei levels, GLenum internalFormat, GLsizei width, GLsizei height) +WebGLTexture::TexStorage(const char* funcName, TexTarget target, GLsizei levels, + GLenum sizedFormat, GLsizei width, GLsizei height, GLsizei depth) { - // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants. - if (texTarget != LOCAL_GL_TEXTURE_2D && texTarget != LOCAL_GL_TEXTURE_CUBE_MAP) - return mContext->ErrorInvalidEnum("texStorage2D: target is not TEXTURE_2D or TEXTURE_CUBE_MAP"); - - if (!ValidateTexStorage(texTarget, levels, internalFormat, width, height, 1, "texStorage2D")) + // Check levels + if (levels < 1) { + mContext->ErrorInvalidValue("%s: `levels` must be >= 1.", funcName); return; - - gl::GLContext* gl = mContext->gl; - gl->MakeCurrent(); - - mContext->GetAndFlushUnderlyingGLErrors(); - gl->fTexStorage2D(texTarget.get(), levels, internalFormat, width, height); - GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); - if (error) { - return mContext->GenerateWarning("texStorage2D generated error %s", mContext->ErrorName(error)); } - SetImmutable(); + if (!width || !height || !depth) { + mContext->ErrorInvalidValue("%s: Dimensions must be non-zero.", funcName); + return; + } - const size_t facesCount = (texTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6; - GLsizei w = width; - GLsizei h = height; - for (size_t l = 0; l < size_t(levels); l++) { - for (size_t f = 0; f < facesCount; f++) { - SetImageInfo(TexImageTargetForTargetAndFace(texTarget, f), - l, w, h, 1, - internalFormat, - WebGLImageDataStatus::UninitializedImageData); + const TexImageTarget testTarget = IsCubeMap() ? LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + : target.get(); + const GLint testLevel = 0; + const GLint border = 0; + + WebGLTexture::ImageInfo* testImageInfo; + if (!ValidateTexImageSpecification(funcName, testTarget, testLevel, width, height, + depth, border, &testImageInfo)) + { + return; + } + MOZ_ASSERT(testImageInfo); + mozilla::Unused << testImageInfo; + + auto dstUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedFormat); + if (!dstUsage) { + mContext->ErrorInvalidEnum("%s: Invalid internalformat: 0x%04x", funcName, + sizedFormat); + return; + } + auto dstFormat = dstUsage->format; + + if (!ValidateTargetForFormat(funcName, mContext, testTarget, dstFormat)) + return; + + if (dstFormat->compression) { + if (!ValidateCompressedTexImageRestrictions(funcName, mContext, testTarget, + testLevel, dstFormat, width, height, + depth)) + { + return; } - w = std::max(1, w / 2); - h = std::max(1, h / 2); } -} -void -WebGLTexture::TexStorage3D(TexTarget texTarget, GLsizei levels, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth) -{ - // GL_INVALID_ENUM is generated if target is not one of the accepted target enumerants. - if (texTarget != LOCAL_GL_TEXTURE_3D) - return mContext->ErrorInvalidEnum("texStorage3D: target is not TEXTURE_3D"); + //////////////////////////////////// - if (!ValidateTexStorage(texTarget, levels, internalFormat, width, height, depth, "texStorage3D")) + const auto lastLevel = levels - 1; + MOZ_ASSERT(lastLevel <= 31, "Right-shift is only defined for bits-1."); + + const uint32_t lastLevelWidth = uint32_t(width) >> lastLevel; + const uint32_t lastLevelHeight = uint32_t(height) >> lastLevel; + const uint32_t lastLevelDepth = uint32_t(depth) >> lastLevel; + + // If these are all zero, then some earlier level was the final 1x1x1 level. + if (!lastLevelWidth && !lastLevelHeight && !lastLevelDepth) { + mContext->ErrorInvalidOperation("%s: Too many levels requested for the given" + " dimensions. (levels: %u, width: %u, height: %u," + " depth: %u)", + funcName, levels, width, height, depth); return; + } - gl::GLContext* gl = mContext->gl; - gl->MakeCurrent(); + //////////////////////////////////// + // Do the thing! - mContext->GetAndFlushUnderlyingGLErrors(); - gl->fTexStorage3D(texTarget.get(), levels, internalFormat, width, height, depth); - GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); + mContext->gl->MakeCurrent(); + + GLenum error = DoTexStorage(mContext->gl, target.get(), levels, sizedFormat, width, + height, depth); + + if (error == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Ran out of memory during texture allocation.", + funcName); + return; + } if (error) { - return mContext->GenerateWarning("texStorage3D generated error %s", mContext->ErrorName(error)); + MOZ_RELEASE_ASSERT(false, "We should have caught all other errors."); + mContext->ErrorInvalidOperation("%s: Unexpected error during texture allocation.", + funcName); + return; } - SetImmutable(); + //////////////////////////////////// + // Update our specification data. - GLsizei w = width; - GLsizei h = height; - GLsizei d = depth; - for (size_t l = 0; l < size_t(levels); l++) { - SetImageInfo(TexImageTargetForTargetAndFace(texTarget, 0), - l, w, h, d, - internalFormat, - WebGLImageDataStatus::UninitializedImageData); - w = std::max(1, w >> 1); - h = std::max(1, h >> 1); - d = std::max(1, d >> 1); - } + const bool isDataInitialized = false; + const WebGLTexture::ImageInfo newInfo(dstUsage, width, height, depth, + isDataInitialized); + SetImageInfosAtLevel(0, newInfo); + + PopulateMipChain(0, levels-1); + + mImmutable = true; } +//////////////////////////////////////// +// Tex(Sub)Image + void -WebGLTexture::TexImage3D(TexImageTarget texImageTarget, GLint level, GLenum internalFormat, - GLsizei width, GLsizei height, GLsizei depth, - GLint border, GLenum unpackFormat, GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv) +WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level, + GLenum internalFormat, GLint border, GLenum unpackFormat, + GLenum unpackType, webgl::TexUnpackBlob* blob) { - void* data; - size_t dataLength; - js::Scalar::Type jsArrayType; - if (maybeView.IsNull()) { - data = nullptr; - dataLength = 0; - jsArrayType = js::Scalar::MaxTypedArrayViewType; - } else { - const auto& view = maybeView.Value(); - ComputeLengthAndData(view, &data, &dataLength, &jsArrayType); - } + //////////////////////////////////// + // Get dest info - const char funcName[] = "texImage3D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 3, funcName)) - return; - - const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D; - - if (!mContext->ValidateTexImage(texImageTarget, level, internalFormat, - 0, 0, 0, - width, height, depth, - border, unpackFormat, unpackType, func, dims)) + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImageSpecification(funcName, target, level, blob->mWidth, + blob->mHeight, blob->mDepth, border, &imageInfo)) { return; } + MOZ_ASSERT(imageInfo); - if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims)) - return; + const webgl::PackingInfo srcPacking = { unpackFormat, unpackType }; - TexInternalFormat effectiveInternalFormat = - EffectiveInternalFormatFromInternalFormatAndType(internalFormat, unpackType); + const auto& fua = mContext->mFormatUsage; + auto dstUsage = fua->GetSizedTexUsage(internalFormat); + if (!dstUsage) { + if (internalFormat != unpackFormat) { + mContext->ErrorInvalidOperation("%s: Unsized internalFormat must match" + " unpack format.", + funcName); + return; + } - if (effectiveInternalFormat == LOCAL_GL_NONE) { - return mContext->ErrorInvalidOperation("texImage3D: bad combination of internalFormat and unpackType"); + dstUsage = fua->GetUnsizedTexUsage(srcPacking); } - // we need to find the exact sized format of the source data. Slightly abusing - // EffectiveInternalFormatFromInternalFormatAndType for that purpose. Really, an unsized source format - // is the same thing as an unsized internalFormat. - TexInternalFormat effectiveSourceFormat = - EffectiveInternalFormatFromInternalFormatAndType(unpackFormat, unpackType); - MOZ_ASSERT(effectiveSourceFormat != LOCAL_GL_NONE); // should have validated unpack format/type combo earlier - const size_t srcbitsPerTexel = GetBitsPerTexel(effectiveSourceFormat); - MOZ_ASSERT((srcbitsPerTexel % 8) == 0); // should not have compressed formats here. - size_t srcTexelSize = srcbitsPerTexel / 8; - - CheckedUint32 checked_neededByteLength = - mContext->GetImageSize(height, width, depth, srcTexelSize, mContext->mPixelStoreUnpackAlignment); - - if (!checked_neededByteLength.isValid()) - return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); - - uint32_t bytesNeeded = checked_neededByteLength.value(); - - if (dataLength && dataLength < bytesNeeded) - return mContext->ErrorInvalidOperation("texImage3D: not enough data for operation (need %d, have %d)", - bytesNeeded, dataLength); - - if (IsImmutable()) { - return mContext->ErrorInvalidOperation( - "texImage3D: disallowed because the texture " - "bound to this target has already been made immutable by texStorage3D"); - } - - gl::GLContext* gl = mContext->gl; - gl->MakeCurrent(); - - GLenum driverUnpackType = LOCAL_GL_NONE; - GLenum driverInternalFormat = LOCAL_GL_NONE; - GLenum driverUnpackFormat = LOCAL_GL_NONE; - DriverFormatsFromEffectiveInternalFormat(gl, - effectiveInternalFormat, - &driverInternalFormat, - &driverUnpackFormat, - &driverUnpackType); - - mContext->GetAndFlushUnderlyingGLErrors(); - gl->fTexImage3D(texImageTarget.get(), level, - driverInternalFormat, - width, height, depth, - 0, driverUnpackFormat, driverUnpackType, - data); - GLenum error = mContext->GetAndFlushUnderlyingGLErrors(); - if (error) { - return mContext->GenerateWarning("texImage3D generated error %s", mContext->ErrorName(error)); - } - - SetImageInfo(texImageTarget, level, - width, height, depth, - effectiveInternalFormat, - data ? WebGLImageDataStatus::InitializedImageData - : WebGLImageDataStatus::UninitializedImageData); -} - -void -WebGLTexture::TexSubImage3D(TexImageTarget texImageTarget, GLint level, - GLint xOffset, GLint yOffset, GLint zOffset, - GLsizei width, GLsizei height, GLsizei depth, - GLenum unpackFormat, GLenum unpackType, - const dom::Nullable& maybeView, - ErrorResult* const out_rv) -{ - if (maybeView.IsNull()) - return mContext->ErrorInvalidValue("texSubImage3D: pixels must not be null!"); - - const auto& view = maybeView.Value(); - void* data; - size_t dataLength; - js::Scalar::Type jsArrayType; - ComputeLengthAndData(view, &data, &dataLength, &jsArrayType); - - const char funcName[] = "texSubImage3D"; - if (!DoesTargetMatchDimensions(mContext, texImageTarget, 3, funcName)) - return; - - const WebGLTexImageFunc func = WebGLTexImageFunc::TexSubImage; - const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D; - - if (!HasImageInfoAt(texImageTarget, level)) { - return mContext->ErrorInvalidOperation("texSubImage3D: no previously defined texture image"); - } - - const WebGLTexture::ImageInfo& imageInfo = ImageInfoAt(texImageTarget, level); - const TexInternalFormat existingEffectiveInternalFormat = imageInfo.EffectiveInternalFormat(); - TexInternalFormat existingUnsizedInternalFormat = LOCAL_GL_NONE; - TexType existingType = LOCAL_GL_NONE; - UnsizedInternalFormatAndTypeFromEffectiveInternalFormat(existingEffectiveInternalFormat, - &existingUnsizedInternalFormat, - &existingType); - - if (!mContext->ValidateTexImage(texImageTarget, level, existingEffectiveInternalFormat.get(), - xOffset, yOffset, zOffset, - width, height, depth, - 0, unpackFormat, unpackType, func, dims)) - { - return; - } - - if (unpackType != existingType) { - return mContext->ErrorInvalidOperation("texSubImage3D: type differs from that of the existing image"); - } - - if (!mContext->ValidateTexInputData(unpackType, jsArrayType, func, dims)) - return; - - const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat); - MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here. - size_t srcTexelSize = bitsPerTexel / 8; - - if (width == 0 || height == 0 || depth == 0) - return; // no effect, we better return right now - - CheckedUint32 checked_neededByteLength = - mContext->GetImageSize(height, width, depth, srcTexelSize, mContext->mPixelStoreUnpackAlignment); - - if (!checked_neededByteLength.isValid()) - return mContext->ErrorInvalidOperation("texSubImage2D: integer overflow computing the needed buffer size"); - - uint32_t bytesNeeded = checked_neededByteLength.value(); - - if (dataLength < bytesNeeded) - return mContext->ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, dataLength); - - if (imageInfo.HasUninitializedImageData()) { - bool coversWholeImage = xOffset == 0 && - yOffset == 0 && - zOffset == 0 && - width == imageInfo.Width() && - height == imageInfo.Height() && - depth == imageInfo.Depth(); - if (coversWholeImage) { - SetImageDataStatus(texImageTarget, level, WebGLImageDataStatus::InitializedImageData); - } else { - if (!EnsureInitializedImageData(texImageTarget, level)) + if (!dstUsage) { + if (!mContext->IsWebGL2()) { + if (!fua->IsInternalFormatEnumValid(internalFormat)) { + mContext->ErrorInvalidValue("%s: Invalid internalformat: 0x%04x", + funcName, internalFormat); return; + } + + if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) { + mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:" + " 0x%04x/0x%04x", + funcName, unpackFormat, unpackType); + return; + } + } + + mContext->ErrorInvalidOperation("%s: Invalid internalformat/format/type:" + " 0x%04x/0x%04x/0x%04x", + funcName, internalFormat, unpackFormat, + unpackType); + return; + } + + const webgl::DriverUnpackInfo* driverUnpackInfo; + if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) { + mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:" + " 0x%04x and 0x%04x/0x%04x", + funcName, internalFormat, unpackFormat, + unpackType); + return; + } + + //////////////////////////////////// + // Get source info + const bool isFunc3D = Is3D(target); + if (!blob->ValidateUnpack(mContext, funcName, isFunc3D, srcPacking)) + return; + + //////////////////////////////////// + // Check that source and dest info are compatible + auto dstFormat = dstUsage->format; + + if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat)) + return; + + if (!mContext->IsWebGL2() && dstFormat->hasDepth) { + if (target != LOCAL_GL_TEXTURE_2D || + blob->mHasData || + level != 0) + { + mContext->ErrorInvalidOperation("%s: With format %s, this function may only" + " be called with target=TEXTURE_2D," + " data=null, and level=0.", + funcName, dstFormat->name); + return; } } - GLenum driverUnpackType = LOCAL_GL_NONE; - GLenum driverInternalFormat = LOCAL_GL_NONE; - GLenum driverUnpackFormat = LOCAL_GL_NONE; - DriverFormatsFromEffectiveInternalFormat(mContext->gl, - existingEffectiveInternalFormat, - &driverInternalFormat, - &driverUnpackFormat, - &driverUnpackType); + //////////////////////////////////// + // Do the thing! - mContext->MakeContextCurrent(); - mContext->gl->fTexSubImage3D(texImageTarget.get(), level, - xOffset, yOffset, zOffset, - width, height, depth, - driverUnpackFormat, driverUnpackType, data); + MOZ_ALWAYS_TRUE( mContext->gl->MakeCurrent() ); + MOZ_ASSERT(mContext->gl->IsCurrent()); + // It's tempting to do allocation first, and TexSubImage second, but this is generally + // slower. + + const ImageInfo newImageInfo(dstUsage, blob->mWidth, blob->mHeight, blob->mDepth, + blob->mHasData); + + const bool isSubImage = false; + const bool needsRespec = (imageInfo->mWidth != newImageInfo.mWidth || + imageInfo->mHeight != newImageInfo.mHeight || + imageInfo->mDepth != newImageInfo.mDepth || + imageInfo->mFormat != newImageInfo.mFormat); + const GLint xOffset = 0; + const GLint yOffset = 0; + const GLint zOffset = 0; + + GLenum glError; + blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level, + driverUnpackInfo, xOffset, yOffset, zOffset, &glError); + + if (glError == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.", + funcName); + return; + } + + if (glError) { + mContext->ErrorInvalidOperation("%s: Unexpected error during upload: 0x%04x", + funcName, glError); + printf_stderr("%s: dui: %x/%x/%x\n", funcName, driverUnpackInfo->internalFormat, + driverUnpackInfo->unpackFormat, driverUnpackInfo->unpackType); + MOZ_ASSERT(false, "Unexpected GL error."); + return; + } + + //////////////////////////////////// + // Update our specification data. + + SetImageInfo(imageInfo, newImageInfo); } +void +WebGLTexture::TexSubImage(const char* funcName, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, + GLenum unpackFormat, GLenum unpackType, + webgl::TexUnpackBlob* blob) +{ + //////////////////////////////////// + // Get dest info + + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset, + blob->mWidth, blob->mHeight, blob->mDepth, &imageInfo)) + { + return; + } + MOZ_ASSERT(imageInfo); + + auto dstUsage = imageInfo->mFormat; + auto dstFormat = dstUsage->format; + + if (dstFormat->compression) { + mContext->ErrorInvalidEnum("%s: Specified TexImage must not be compressed.", + funcName); + return; + } + + if (!mContext->IsWebGL2() && dstFormat->hasDepth) { + mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of" + " format %s.", + funcName, dstFormat->name); + return; + } + + //////////////////////////////////// + // Get source info + + const webgl::PackingInfo srcPacking = { unpackFormat, unpackType }; + const webgl::DriverUnpackInfo* driverUnpackInfo; + if (!dstUsage->IsUnpackValid(srcPacking, &driverUnpackInfo)) { + if (!mContext->IsWebGL2()) { + const auto& fua = mContext->mFormatUsage; + if (!fua->AreUnpackEnumsValid(unpackFormat, unpackType)) { + mContext->ErrorInvalidEnum("%s: Invalid unpack format/type:" + " 0x%04x/0x%04x", + funcName, unpackFormat, unpackType); + return; + } + } + + mContext->ErrorInvalidOperation("%s: Mismatched internalFormat and format/type:" + " %s and 0x%04x/0x%04x", + funcName, dstFormat->name, unpackFormat, + unpackType); + return; + } + + const bool isFunc3D = Is3D(target); + if (!blob->ValidateUnpack(mContext, funcName, isFunc3D, srcPacking)) + return; + + //////////////////////////////////// + // Do the thing! + + mContext->gl->MakeCurrent(); + + bool uploadWillInitialize; + if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset, + yOffset, zOffset, blob->mWidth, + blob->mHeight, blob->mDepth, imageInfo, + &uploadWillInitialize)) + { + return; + } + + const bool isSubImage = true; + const bool needsRespec = false; + + GLenum glError; + blob->TexOrSubImage(isSubImage, needsRespec, funcName, this, target, level, + driverUnpackInfo, xOffset, yOffset, zOffset, &glError); + + if (glError == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Driver ran out of memory during upload.", + funcName); + return; + } + + if (glError) { + mContext->ErrorInvalidOperation("%s: Unexpected error during upload: 0x04x", + funcName, glError); + MOZ_ASSERT(false, "Unexpected GL error."); + return; + } + + //////////////////////////////////// + // Update our specification data? + + if (uploadWillInitialize) { + imageInfo->SetIsDataInitialized(true, this); + } +} + +//////////////////////////////////////// +// CompressedTex(Sub)Image + +void +WebGLTexture::CompressedTexImage(const char* funcName, TexImageTarget target, GLint level, + GLenum internalFormat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, + const dom::ArrayBufferViewOrSharedArrayBufferView& view) +{ + //////////////////////////////////// + // Get dest info + + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth, + border, &imageInfo)) + { + return; + } + MOZ_ASSERT(imageInfo); + + auto usage = mContext->mFormatUsage->GetSizedTexUsage(internalFormat); + if (!usage) { + mContext->ErrorInvalidEnum("%s: Invalid internalFormat: 0x%04x", funcName, + internalFormat); + return; + } + + auto format = usage->format; + if (!format->compression) { + mContext->ErrorInvalidEnum("%s: Specified internalFormat must be compressed.", + funcName); + return; + } + + if (!ValidateTargetForFormat(funcName, mContext, target, format)) + return; + + //////////////////////////////////// + // Get source info + + void* mutData; + size_t dataSize; + js::Scalar::Type jsType; + ComputeLengthAndData(view, &mutData, &dataSize, &jsType); + const void* data = mutData; + + if (!ValidateCompressedTexUnpack(mContext, funcName, width, height, depth, format, + dataSize)) + { + return; + } + + //////////////////////////////////// + // Check that source is compatible with dest + + if (!ValidateCompressedTexImageRestrictions(funcName, mContext, target, level, format, + width, height, depth)) + { + return; + } + + //////////////////////////////////// + // Do the thing! + + mContext->gl->MakeCurrent(); + + GLenum error = DoCompressedTexImage(mContext->gl, target, level, internalFormat, + width, height, depth, border, dataSize, data); + if (error == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName); + return; + } + if (error) { + MOZ_RELEASE_ASSERT(false, "We should have caught all other errors."); + mContext->GenerateWarning("%s: Unexpected error during texture upload. Context" + " lost.", + funcName); + mContext->ForceLoseContext(); + return; + } + + //////////////////////////////////// + // Update our specification data. + + const bool isDataInitialized = true; + const ImageInfo newImageInfo(usage, width, height, depth, isDataInitialized); + SetImageInfo(imageInfo, newImageInfo); +} + +static inline bool +IsSubImageBlockAligned(const webgl::CompressedFormatInfo* compression, + const WebGLTexture::ImageInfo* imageInfo, GLint xOffset, + GLint yOffset, uint32_t width, uint32_t height) +{ + if (xOffset % compression->blockWidth != 0 || + yOffset % compression->blockHeight != 0) + { + return false; + } + + if (width % compression->blockWidth != 0 && xOffset + width != imageInfo->mWidth) + return false; + + if (height % compression->blockHeight != 0 && yOffset + height != imageInfo->mHeight) + return false; + + return true; +} + +void +WebGLTexture::CompressedTexSubImage(const char* funcName, TexImageTarget target, + GLint level, GLint xOffset, GLint yOffset, + GLint zOffset, GLsizei width, GLsizei height, + GLsizei depth, GLenum sizedUnpackFormat, + const dom::ArrayBufferViewOrSharedArrayBufferView& view) +{ + //////////////////////////////////// + // Get dest info + + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset, + width, height, depth, &imageInfo)) + { + return; + } + MOZ_ASSERT(imageInfo); + + auto dstUsage = imageInfo->mFormat; + auto dstFormat = dstUsage->format; + + //////////////////////////////////// + // Get source info + + void* mutData; + size_t dataSize; + js::Scalar::Type jsType; + ComputeLengthAndData(view, &mutData, &dataSize, &jsType); + const void* data = mutData; + + auto srcUsage = mContext->mFormatUsage->GetSizedTexUsage(sizedUnpackFormat); + if (!srcUsage->format->compression) { + mContext->ErrorInvalidEnum("%s: Specified format must be compressed.", funcName); + return; + } + + if (srcUsage != dstUsage) { + mContext->ErrorInvalidOperation("%s: `format` must match the format of the" + " existing texture image.", + funcName); + return; + } + + auto format = srcUsage->format; + MOZ_ASSERT(format == dstFormat); + if (!ValidateCompressedTexUnpack(mContext, funcName, width, height, depth, format, + dataSize)) + { + return; + } + + //////////////////////////////////// + // Check that source is compatible with dest + + switch (format->compression->family) { + // Forbidden: + case webgl::CompressionFamily::ETC1: + case webgl::CompressionFamily::ATC: + mContext->ErrorInvalidOperation("%s: Format does not allow sub-image" + " updates.", funcName); + return; + + // Block-aligned: + case webgl::CompressionFamily::ES3: // Yes, the ES3 formats don't match the ES3 + case webgl::CompressionFamily::S3TC: // default behavior. + if (!IsSubImageBlockAligned(dstFormat->compression, imageInfo, xOffset, yOffset, + width, height)) + { + mContext->ErrorInvalidOperation("%s: Format requires block-aligned sub-image" + " updates.", + funcName); + return; + } + break; + + // Full-only: (The ES3 default) + default: // PVRTC + if (xOffset || yOffset || + uint32_t(width) != imageInfo->mWidth || + uint32_t(height) != imageInfo->mHeight) + { + mContext->ErrorInvalidOperation("%s: Format does not allow partial sub-image" + " updates.", + funcName); + return; + } + break; + } + + //////////////////////////////////// + // Do the thing! + + mContext->gl->MakeCurrent(); + + bool uploadWillInitialize; + if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset, + yOffset, zOffset, width, height, depth, + imageInfo, &uploadWillInitialize)) + { + return; + } + + GLenum error = DoCompressedTexSubImage(mContext->gl, target, level, xOffset, yOffset, + zOffset, width, height, depth, + sizedUnpackFormat, dataSize, data); + if (error == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Ran out of memory during upload.", funcName); + return; + } + if (error) { + MOZ_RELEASE_ASSERT(false, "We should have caught all other errors."); + mContext->GenerateWarning("%s: Unexpected error during texture upload. Context" + " lost.", + funcName); + mContext->ForceLoseContext(); + return; + } + + //////////////////////////////////// + // Update our specification data? + + if (uploadWillInitialize) { + imageInfo->SetIsDataInitialized(true, this); + } +} + +//////////////////////////////////////// +// CopyTex(Sub)Image + +static bool +ValidateCopyTexImageFormats(WebGLContext* webgl, const char* funcName, + const webgl::FormatInfo* srcFormat, + const webgl::FormatInfo* dstFormat) +{ + MOZ_ASSERT(!srcFormat->compression); + if (dstFormat->compression) { + webgl->ErrorInvalidEnum("%s: Specified destination must not have a compressed" + " format.", + funcName); + return false; + } + + if (dstFormat->effectiveFormat == webgl::EffectiveFormat::RGB9_E5) { + webgl->ErrorInvalidOperation("%s: RGB9_E5 is an invalid destination for" + " CopyTex(Sub)Image. (GLES 3.0.4 p145)", + funcName); + return false; + } + + if (!DoChannelsMatchForCopyTexImage(srcFormat, dstFormat)) { + webgl->ErrorInvalidOperation("%s: Destination channels must be compatible with" + " source channels. (GLES 3.0.4 p140 Table 3.16)", + funcName); + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// + +class ScopedCopyTexImageSource +{ + WebGLContext* const mWebGL; + GLuint mRB; + GLuint mFB; + +public: + ScopedCopyTexImageSource(WebGLContext* webgl, const char* funcName, uint32_t srcWidth, + uint32_t srcHeight, const webgl::FormatInfo* srcFormat, + const webgl::FormatUsageInfo* dstUsage); + ~ScopedCopyTexImageSource(); +}; + +ScopedCopyTexImageSource::ScopedCopyTexImageSource(WebGLContext* webgl, + const char* funcName, + uint32_t srcWidth, uint32_t srcHeight, + const webgl::FormatInfo* srcFormat, + const webgl::FormatUsageInfo* dstUsage) + : mWebGL(webgl) + , mRB(0) + , mFB(0) +{ + switch (dstUsage->format->unsizedFormat) { + case webgl::UnsizedFormat::L: + case webgl::UnsizedFormat::A: + case webgl::UnsizedFormat::LA: + webgl->GenerateWarning("%s: Copying to a LUMINANCE, ALPHA, or LUMINANCE_ALPHA" + " is deprecated, and has severely reduced performance" + " on some platforms.", + funcName); + break; + + default: + MOZ_ASSERT(!dstUsage->textureSwizzleRGBA); + return; + } + + if (!dstUsage->textureSwizzleRGBA) + return; + + gl::GLContext* gl = webgl->gl; + + GLenum sizedFormat; + + switch (srcFormat->componentType) { + case webgl::ComponentType::NormUInt: + sizedFormat = LOCAL_GL_RGBA8; + break; + + case webgl::ComponentType::Float: + if (webgl->IsExtensionEnabled(WebGLExtensionID::WEBGL_color_buffer_float)) { + sizedFormat = LOCAL_GL_RGBA32F; + break; + } + + if (webgl->IsExtensionEnabled(WebGLExtensionID::EXT_color_buffer_half_float)) { + sizedFormat = LOCAL_GL_RGBA16F; + break; + } + MOZ_CRASH("Should be able to request CopyTexImage from Float."); + + default: + MOZ_CRASH("Should be able to request CopyTexImage from this type."); + } + + gl::ScopedTexture scopedTex(gl); + gl::ScopedBindTexture scopedBindTex(gl, scopedTex.Texture(), LOCAL_GL_TEXTURE_2D); + + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); + + GLint blitSwizzle[4] = {LOCAL_GL_ZERO}; + switch (dstUsage->format->unsizedFormat) { + case webgl::UnsizedFormat::L: + blitSwizzle[0] = LOCAL_GL_RED; + break; + + case webgl::UnsizedFormat::A: + blitSwizzle[0] = LOCAL_GL_ALPHA; + break; + + case webgl::UnsizedFormat::LA: + blitSwizzle[0] = LOCAL_GL_RED; + blitSwizzle[1] = LOCAL_GL_ALPHA; + break; + + default: + MOZ_CRASH("Unhandled unsizedFormat."); + } + + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_R, blitSwizzle[0]); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_G, blitSwizzle[1]); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_B, blitSwizzle[2]); + gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_SWIZZLE_A, blitSwizzle[3]); + + gl->fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, sizedFormat, 0, 0, srcWidth, + srcHeight, 0); + + // Now create the swizzled FB we'll be exposing. + + GLuint rgbaRB = 0; + gl->fGenRenderbuffers(1, &rgbaRB); + gl::ScopedBindRenderbuffer scopedRB(gl, rgbaRB); + gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, sizedFormat, srcWidth, srcHeight); + + GLuint rgbaFB = 0; + gl->fGenFramebuffers(1, &rgbaFB); + gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, rgbaFB); + gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0, + LOCAL_GL_RENDERBUFFER, rgbaRB); + + const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER); + if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) { + MOZ_CRASH("Temp framebuffer is not complete."); + } + + // Restore RB binding. + scopedRB.Unwrap(); // This function should really have a better name. + + // Draw-blit rgbaTex into rgbaFB. + const gfx::IntSize srcSize(srcWidth, srcHeight); + gl->BlitHelper()->DrawBlitTextureToFramebuffer(scopedTex.Texture(), rgbaFB, + srcSize, srcSize); + + // Restore Tex2D binding and destroy the temp tex. + scopedBindTex.Unwrap(); + scopedTex.Unwrap(); + + // Leave RB and FB alive, and FB bound. + mRB = rgbaRB; + mFB = rgbaFB; +} + +template +static inline GLenum +ToGLHandle(const T& obj) +{ + return (obj ? obj->mGLName : 0); +} + +ScopedCopyTexImageSource::~ScopedCopyTexImageSource() +{ + if (!mFB) { + MOZ_ASSERT(!mRB); + return; + } + MOZ_ASSERT(mRB); + + gl::GLContext* gl = mWebGL->gl; + + // If we're swizzling, it's because we're on a GL core (3.2+) profile, which has + // split framebuffer support. + gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, + ToGLHandle(mWebGL->mBoundDrawFramebuffer)); + gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, + ToGLHandle(mWebGL->mBoundReadFramebuffer)); + + gl->fDeleteFramebuffers(1, &mFB); + gl->fDeleteRenderbuffers(1, &mRB); +} + +//////////////////////////////////////////////////////////////////////////////// + +// There is no CopyTexImage3D. +void +WebGLTexture::CopyTexImage2D(TexImageTarget target, GLint level, GLenum internalFormat, + GLint x, GLint y, GLsizei width, GLsizei height, + GLint border) +{ + const char funcName[] = "CopyTexImage2D"; + + const uint8_t depth = 1; + + //////////////////////////////////// + // Get dest info + + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImageSpecification(funcName, target, level, width, height, depth, + border, &imageInfo)) + { + return; + } + MOZ_ASSERT(imageInfo); + + //////////////////////////////////// + // Get source info + + const webgl::FormatUsageInfo* srcUsage; + uint32_t srcWidth; + uint32_t srcHeight; + if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight)) + return; + auto srcFormat = srcUsage->format; + + //////////////////////////////////// + // Check that source and dest info are compatible + + const auto& fua = mContext->mFormatUsage; + + auto dstUsage = fua->GetSizedTexUsage(internalFormat); + if (!dstUsage) { + // It must be an unsized format then... + webgl::PackingInfo pi = {internalFormat, 0}; + + switch (srcFormat->componentType) { + case webgl::ComponentType::NormUInt: + pi.type = LOCAL_GL_UNSIGNED_BYTE; + break; + + case webgl::ComponentType::Float: + pi.type = LOCAL_GL_FLOAT; + break; + + default: + break; + } + + dstUsage = fua->GetUnsizedTexUsage(pi); + } + + if (!dstUsage) { + mContext->ErrorInvalidEnum("%s: Invalid internalFormat 0x%04x for FB format %s.", + funcName, internalFormat, srcFormat->name); + return; + } + auto dstFormat = dstUsage->format; + + if (!ValidateTargetForFormat(funcName, mContext, target, dstFormat)) + return; + + if (!mContext->IsWebGL2() && dstFormat->hasDepth) { + mContext->ErrorInvalidOperation("%s: Function may not be called with format %s.", + funcName, dstFormat->name); + return; + } + + if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat)) + return; + + //////////////////////////////////// + // Do the thing! + + gl::GLContext* gl = mContext->gl; + gl->MakeCurrent(); + + ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight, + srcFormat, dstUsage); + + uint32_t readX, readY; + uint32_t writeX, writeY; + uint32_t rwWidth, rwHeight; + Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth); + Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight); + + GLenum error; + if (rwWidth == uint32_t(width) && rwHeight == uint32_t(height)) { + error = DoCopyTexImage2D(gl, target, level, internalFormat, x, y, width, height, + border); + } else { + // 1. Zero the texture data. + // 2. CopyTexSubImage the subrect. + + const bool respecifyTexture = true; + const uint8_t zOffset = 0; + if (!ZeroTextureData(mContext, funcName, respecifyTexture, target, level, + dstUsage, 0, 0, zOffset, width, height, depth)) + { + mContext->ErrorOutOfMemory("%s: Failed to zero texture data.", funcName); + MOZ_ASSERT(false, "Failed to zero texture data."); + return; + } + + if (!rwWidth || !rwHeight) { + // There aren't any, so we're 'done'. + mContext->DummyFramebufferOperation(funcName); + return; + } + + error = DoCopyTexSubImage(gl, target, level, writeX, writeY, zOffset, readX, + readY, rwWidth, rwHeight); + } + + if (error == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", + funcName); + return; + } + if (error) { + MOZ_RELEASE_ASSERT(false, "We should have caught all other errors."); + mContext->GenerateWarning("%s: Unexpected error during texture copy. Context" + " lost.", + funcName); + mContext->ForceLoseContext(); + return; + } + + //////////////////////////////////// + // Update our specification data. + + const bool isDataInitialized = true; + const ImageInfo newImageInfo(dstUsage, width, height, depth, isDataInitialized); + SetImageInfo(imageInfo, newImageInfo); +} + +void +WebGLTexture::CopyTexSubImage(const char* funcName, TexImageTarget target, GLint level, + GLint xOffset, GLint yOffset, GLint zOffset, GLint x, + GLint y, GLsizei width, GLsizei height) +{ + const GLsizei depth = 1; + + //////////////////////////////////// + // Get dest info + + WebGLTexture::ImageInfo* imageInfo; + if (!ValidateTexImageSelection(funcName, target, level, xOffset, yOffset, zOffset, + width, height, depth, &imageInfo)) + { + return; + } + MOZ_ASSERT(imageInfo); + + auto dstUsage = imageInfo->mFormat; + MOZ_ASSERT(dstUsage); + auto dstFormat = dstUsage->format; + + if (!mContext->IsWebGL2() && dstFormat->hasDepth) { + mContext->ErrorInvalidOperation("%s: Function may not be called on a texture of" + " format %s.", + funcName, dstFormat->name); + return; + } + + //////////////////////////////////// + // Get source info + + const webgl::FormatUsageInfo* srcUsage; + uint32_t srcWidth; + uint32_t srcHeight; + if (!mContext->ValidateCurFBForRead(funcName, &srcUsage, &srcWidth, &srcHeight)) + return; + auto srcFormat = srcUsage->format; + + //////////////////////////////////// + // Check that source and dest info are compatible + + if (!ValidateCopyTexImageFormats(mContext, funcName, srcFormat, dstFormat)) + return; + + //////////////////////////////////// + // Do the thing! + + mContext->gl->MakeCurrent(); + + ScopedCopyTexImageSource maybeSwizzle(mContext, funcName, srcWidth, srcHeight, + srcFormat, dstUsage); + + uint32_t readX, readY; + uint32_t writeX, writeY; + uint32_t rwWidth, rwHeight; + Intersect(srcWidth, x, width, &readX, &writeX, &rwWidth); + Intersect(srcHeight, y, height, &readY, &writeY, &rwHeight); + + if (!rwWidth || !rwHeight) { + // There aren't any, so we're 'done'. + mContext->DummyFramebufferOperation(funcName); + return; + } + + bool uploadWillInitialize; + if (!EnsureImageDataInitializedForUpload(this, funcName, target, level, xOffset, + yOffset, zOffset, width, height, depth, + imageInfo, &uploadWillInitialize)) + { + return; + } + + GLenum error = DoCopyTexSubImage(mContext->gl, target, level, xOffset + writeX, + yOffset + writeY, zOffset, readX, readY, rwWidth, + rwHeight); + + if (error == LOCAL_GL_OUT_OF_MEMORY) { + mContext->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", + funcName); + return; + } + if (error) { + MOZ_RELEASE_ASSERT(false, "We should have caught all other errors."); + mContext->GenerateWarning("%s: Unexpected error during texture copy. Context" + " lost.", + funcName); + mContext->ForceLoseContext(); + return; + } + + //////////////////////////////////// + // Update our specification data? + + if (uploadWillInitialize) { + imageInfo->SetIsDataInitialized(true, this); + } +} } // namespace mozilla diff --git a/dom/canvas/WebGLTimerQuery.cpp b/dom/canvas/WebGLTimerQuery.cpp index 52aa256167..5bdc1648d3 100644 --- a/dom/canvas/WebGLTimerQuery.cpp +++ b/dom/canvas/WebGLTimerQuery.cpp @@ -50,7 +50,7 @@ WebGLTimerQuery::Delete() WebGLContext* WebGLTimerQuery::GetParentObject() const { - return Context(); + return mContext; } diff --git a/dom/canvas/WebGLTransformFeedback.cpp b/dom/canvas/WebGLTransformFeedback.cpp index a7c97ca420..6149b3ebb7 100644 --- a/dom/canvas/WebGLTransformFeedback.cpp +++ b/dom/canvas/WebGLTransformFeedback.cpp @@ -41,7 +41,7 @@ WebGLTransformFeedback::Delete() WebGLContext* WebGLTransformFeedback::GetParentObject() const { - return Context(); + return mContext; } JSObject* diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index c0aa29c722..424697e74e 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -18,12 +18,10 @@ typedef bool WebGLboolean; namespace mozilla { /* - * WebGLContextFakeBlackStatus and WebGLTextureFakeBlackStatus are enums to - * track what needs to use a dummy 1x1 black texture, which we refer to as a - * 'fake black' texture. + * WebGLTextureFakeBlackStatus is an enum to track what needs to use a dummy 1x1 black + * texture, which we refer to as a 'fake black' texture. * - * There are generally two things that can cause us to use such 'fake black' - * textures: + * There are two things that can cause us to use such 'fake black' textures: * * (1) OpenGL ES rules on sampling incomplete textures specify that they * must be sampled as RGBA(0, 0, 0, 1) (opaque black). We have to implement these rules @@ -38,23 +36,12 @@ namespace mozilla { * uninitialized image data must be exposed to WebGL as if it were filled * with zero bytes, which means it's either opaque or transparent black * depending on whether the image format has alpha. - * - * Why are there _two_ separate enums there, WebGLContextFakeBlackStatus - * and WebGLTextureFakeBlackStatus? That's because each texture must know the precise - * reason why it needs to be faked (incomplete texture vs. uninitialized image data), - * whereas the WebGL context can only know whether _any_ faking is currently needed at all. */ -enum class WebGLContextFakeBlackStatus : uint8_t { - Unknown, - NotNeeded, - Needed -}; -enum class WebGLTextureFakeBlackStatus : uint8_t { - Unknown, - NotNeeded, - IncompleteTexture, - UninitializedImageData +enum class FakeBlackType : uint8_t { + None, + RGBA0001, // Incomplete textures and uninitialized no-alpha color textures. + RGBA0000, // Uninitialized with-alpha color textures. }; /* @@ -115,6 +102,7 @@ enum class WebGLTexelFormat : uint8_t { RA32F, // OES_texture_float // 3-channel formats RGB8, + RGBX8, // used for DOM elements. Source format only. BGRX8, // used for DOM elements. Source format only. RGB565, RGB16F, // OES_texture_half_float diff --git a/dom/canvas/WebGLVertexArray.h b/dom/canvas/WebGLVertexArray.h index 2e3beb1c0f..08c564fa6c 100644 --- a/dom/canvas/WebGLVertexArray.h +++ b/dom/canvas/WebGLVertexArray.h @@ -47,7 +47,7 @@ public: bool IsVertexArray(); WebGLContext* GetParentObject() const { - return Context(); + return mContext; } virtual JSObject* WrapObject(JSContext* cx, JS::Handle givenProto) override; diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index 5ae1351a6c..67a87e4eac 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -60,6 +60,7 @@ UNIFIED_SOURCES += [ # WebGL Sources UNIFIED_SOURCES += [ 'MurmurHash3.cpp', + 'TexUnpackBlob.cpp', 'WebGL1Context.cpp', 'WebGL1ContextBuffers.cpp', 'WebGL1ContextUniforms.cpp', diff --git a/dom/canvas/test/webgl-conformance/conformance/extensions/ext-sRGB.html b/dom/canvas/test/webgl-conformance/conformance/extensions/ext-sRGB.html index 2feb17e830..e5db221fff 100644 --- a/dom/canvas/test/webgl-conformance/conformance/extensions/ext-sRGB.html +++ b/dom/canvas/test/webgl-conformance/conformance/extensions/ext-sRGB.html @@ -78,26 +78,27 @@ function expectResult(target, successMessage, failureMessage) { } } -function createGreysRGBTexture(gl, color) { +function createGreySRGBATexture(gl, color) { var numPixels = gl.drawingBufferWidth * gl.drawingBufferHeight; - var size = numPixels * 3; + var size = numPixels * 4; var buf = new Uint8Array(size); for (var ii = 0; ii < numPixels; ++ii) { var off = ii * 3; buf[off + 0] = color; buf[off + 1] = color; buf[off + 2] = color; + buf[off + 3] = 0xff; } var tex = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, tex); gl.texImage2D(gl.TEXTURE_2D, 0, - ext.SRGB_EXT, + ext.SRGB_ALPHA_EXT, gl.drawingBufferWidth, gl.drawingBufferHeight, 0, - ext.SRGB_EXT, + ext.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, buf); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); @@ -266,11 +267,11 @@ function runTextureReadConversionTest() { gl.uniform1i(gl.getUniformLocation(program, "tex2d"), 0); for (var ii = 0; ii < conversions.length; ii++) { - var tex = createGreysRGBTexture(gl, conversions[ii][0]); + var tex = createGreySRGBATexture(gl, conversions[ii][0]); wtu.drawQuad(gl); expectResult(conversions[ii][1], - "sRGB texture read returned correct data", - "sRGB texture read returned incorrect data"); + "sRGBA texture read returned correct data", + "sRGBA texture read returned incorrect data"); } } @@ -279,13 +280,14 @@ function runFramebufferTextureConversionTest() { debug("Test the conversion of colors from linear to sRGB on framebuffer (texture) write"); var program = wtu.setupProgram(gl, ['vertexShader', 'fragmentShader'], ['aPosition'], [0]); - var tex = createGreysRGBTexture(gl, 0); + var tex = createGreySRGBATexture(gl, 0); var fbo = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); glErrorShouldBe(gl, gl.NO_ERROR); shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT)', 'ext.SRGB_EXT'); + glErrorShouldBe(gl, gl.NO_ERROR); shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); // Draw @@ -307,6 +309,7 @@ function runFramebufferTextureConversionTest() { "framebuffer (texture) read returned incorrect data"); } + glErrorShouldBe(gl, gl.NO_ERROR); gl.bindFramebuffer(gl.FRAMEBUFFER, null); } @@ -327,6 +330,7 @@ function runFramebufferRenderbufferConversionTest() { glErrorShouldBe(gl, gl.NO_ERROR); shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT)', 'ext.SRGB_EXT'); + glErrorShouldBe(gl, gl.NO_ERROR); shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); return fbo; @@ -352,6 +356,8 @@ function runFramebufferRenderbufferConversionTest() { "framebuffer (renderbuffer) read returned the correct data", "framebuffer (renderbuffer) read returned incorrect data"); } + + glErrorShouldBe(gl, gl.NO_ERROR); } debug(""); diff --git a/dom/canvas/test/webgl-conformance/conformance/extensions/oes-texture-float.html b/dom/canvas/test/webgl-conformance/conformance/extensions/oes-texture-float.html index a20ff4ccb0..890ab4375b 100644 --- a/dom/canvas/test/webgl-conformance/conformance/extensions/oes-texture-float.html +++ b/dom/canvas/test/webgl-conformance/conformance/extensions/oes-texture-float.html @@ -131,7 +131,8 @@ function runTextureCreationTest(testProgram, extensionEnabled) } gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, data); if (expectFailure) { - glErrorShouldBe(gl, gl.INVALID_ENUM, "floating-point texture allocation must be disallowed if OES_texture_float isn't enabled"); + wtu.glErrorShouldBeIn(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], + "floating-point texture allocation must be disallowed if OES_texture_float isn't enabled"); return; } else { glErrorShouldBe(gl, gl.NO_ERROR, "floating-point texture allocation should succeed if OES_texture_float is enabled"); diff --git a/dom/canvas/test/webgl-conformance/conformance/misc/object-deletion-behaviour.html b/dom/canvas/test/webgl-conformance/conformance/misc/object-deletion-behaviour.html index 579910983f..2fc4a61395 100644 --- a/dom/canvas/test/webgl-conformance/conformance/misc/object-deletion-behaviour.html +++ b/dom/canvas/test/webgl-conformance/conformance/misc/object-deletion-behaviour.html @@ -76,7 +76,7 @@ shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteTexture(tex)"); // Deleting a texture bound to the currently-bound fbo is the same as // detaching the textue from fbo first, then delete the texture. shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.NONE"); -shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)"); +shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", "null"); shouldBeFalse("gl.isTexture(tex)"); shouldBeNull("gl.getParameter(gl.TEXTURE_BINDING_2D)"); shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindTexture(gl.TEXTURE_2D, tex)"); @@ -128,7 +128,7 @@ shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteRenderbuffer(rbo)"); // Deleting a renderbuffer bound to the currently-bound fbo is the same as // detaching the renderbuffer from fbo first, then delete the renderbuffer. shouldBe("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)", "gl.NONE"); -shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)"); +shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", "null"); shouldBeFalse("gl.isRenderbuffer(rbo)"); shouldBeNull("gl.getParameter(gl.RENDERBUFFER_BINDING)"); shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindRenderbuffer(gl.RENDERBUFFER, rbo)"); @@ -263,7 +263,7 @@ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) { shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, fbo)"); shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)"); - shouldGenerateGLError(gl, gl.NONE, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)"); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)"); shouldNotBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); // Bind backbuffer. shouldGenerateGLError(gl, gl.NO_ERROR, "gl.bindFramebuffer(gl.FRAMEBUFFER, null)"); diff --git a/dom/canvas/test/webgl-conformance/conformance/more/functions/texImage2DBadArgs.html b/dom/canvas/test/webgl-conformance/conformance/more/functions/texImage2DBadArgs.html index 4523ac0971..750e681385 100644 --- a/dom/canvas/test/webgl-conformance/conformance/more/functions/texImage2DBadArgs.html +++ b/dom/canvas/test/webgl-conformance/conformance/more/functions/texImage2DBadArgs.html @@ -58,53 +58,53 @@ function teardown(gl, tex) { gl.deleteTexture(tex); } -function testrunner(gl, expected, desc, fn) { +function testrunner(gl, expectedList, desc, fn) { var tex = setup(gl); fn(); - glErrorShouldBe(gl, expected, desc); + wtu.glErrorShouldBeIn(gl, expectedList, desc); teardown(gl, tex); } var data = new Uint8Array(4); - testrunner(gl, gl.INVALID_OPERATION, "not enough data", function(){ + testrunner(gl, [gl.INVALID_OPERATION], "not enough data", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2,1,0,gl.RGBA,gl.UNSIGNED_BYTE, data); }); - testrunner(gl, gl.INVALID_OPERATION, "not enough data", function(){ + testrunner(gl, [gl.INVALID_OPERATION], "not enough data", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,2,0,gl.RGBA,gl.UNSIGNED_BYTE, data); }); - testrunner(gl, gl.INVALID_ENUM, "bad target", function(){ + testrunner(gl, [gl.INVALID_ENUM], "bad target", function(){ gl.texImage2D(gl.FLOAT, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, null); }); - testrunner(gl, gl.INVALID_ENUM, "bad internal format/format", function(){ + testrunner(gl, [gl.INVALID_ENUM, gl.INVALID_VALUE], "bad internal format/format", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.FLOAT, 1,1,0,gl.FLOAT,gl.UNSIGNED_BYTE, null); }); - testrunner(gl, gl.INVALID_VALUE, "border > 0", function(){ + testrunner(gl, [gl.INVALID_VALUE], "border > 0", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,48,gl.RGBA,gl.UNSIGNED_BYTE, null); }); // The spec says zero size is OK. If you disagree please list the section // in the spec that details this issue. - testrunner(gl, gl.NO_ERROR, "zero size", function(){ + testrunner(gl, [gl.NO_ERROR], "zero size", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0,0,0,gl.RGBA,gl.UNSIGNED_BYTE, null); }); - testrunner(gl, gl.INVALID_VALUE, "negative width", function(){ + testrunner(gl, [gl.INVALID_VALUE], "negative width", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, -1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, null); }); - testrunner(gl, gl.INVALID_VALUE, "negative height", function(){ + testrunner(gl, [gl.INVALID_VALUE], "negative height", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,-1,0,gl.RGBA,gl.UNSIGNED_BYTE, null); }); - testrunner(gl, gl.INVALID_ENUM, "bad format", function(){ + testrunner(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], "bad format", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.FLOAT,gl.UNSIGNED_BYTE, null); }); - testrunner(gl, gl.INVALID_ENUM, "bad type", function(){ + testrunner(gl, [gl.INVALID_ENUM], "bad type", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.TEXTURE_2D, null); }); - testrunner(gl, gl.INVALID_OPERATION, "not enough data", function(){ + testrunner(gl, [gl.INVALID_OPERATION], "not enough data", function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_BYTE, new Uint8Array(3)); }); - testrunner(gl, gl.INVALID_OPERATION, "format and type incompatible",function(){ + testrunner(gl, [gl.INVALID_OPERATION], "format and type incompatible",function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1,1,0,gl.RGBA,gl.UNSIGNED_SHORT_5_6_5, null); }); - testrunner(gl, gl.INVALID_OPERATION, "format and type incompatible",function(){ + testrunner(gl, [gl.INVALID_OPERATION], "format and type incompatible",function(){ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 1,1,0,gl.RGB,gl.UNSIGNED_SHORT_4_4_4_4, null); }); diff --git a/dom/canvas/test/webgl-conformance/conformance/more/functions/texSubImage2DBadArgs.html b/dom/canvas/test/webgl-conformance/conformance/more/functions/texSubImage2DBadArgs.html index c88c788c57..edfd10f480 100644 --- a/dom/canvas/test/webgl-conformance/conformance/more/functions/texSubImage2DBadArgs.html +++ b/dom/canvas/test/webgl-conformance/conformance/more/functions/texSubImage2DBadArgs.html @@ -31,6 +31,7 @@ OTHER DEALINGS IN THE SOFTWARE. + - - - @@ -97,7 +34,7 @@ void main() { diff --git a/toolkit/mozapps/installer/packager.py b/toolkit/mozapps/installer/packager.py index 23534b8b76..fe22d1ab26 100644 --- a/toolkit/mozapps/installer/packager.py +++ b/toolkit/mozapps/installer/packager.py @@ -313,7 +313,7 @@ def main(): if is_native(args.source): launcher.tooldir = args.source elif not buildconfig.substs['CROSS_COMPILE']: - launcher.tooldir = buildconfig.substs['LIBXUL_DIST'] + launcher.tooldir = mozpath.join(buildconfig.topobjdir, 'dist') with errors.accumulate(): finder_args = dict( diff --git a/toolkit/mozapps/installer/upload-files.mk b/toolkit/mozapps/installer/upload-files.mk index 7e9cccd3f4..fc6b1d2fb2 100644 --- a/toolkit/mozapps/installer/upload-files.mk +++ b/toolkit/mozapps/installer/upload-files.mk @@ -502,29 +502,10 @@ endif ifeq ($(MOZ_PKG_FORMAT),DMG) PKG_SUFFIX = .dmg -PKG_DMG_FLAGS = -ifneq (,$(MOZ_PKG_MAC_DSSTORE)) -PKG_DMG_FLAGS += --copy '$(MOZ_PKG_MAC_DSSTORE):/.DS_Store' -endif -ifneq (,$(MOZ_PKG_MAC_BACKGROUND)) -PKG_DMG_FLAGS += --mkdir /.background --copy '$(MOZ_PKG_MAC_BACKGROUND):/.background' -endif -ifneq (,$(MOZ_PKG_MAC_ICON)) -PKG_DMG_FLAGS += --icon '$(MOZ_PKG_MAC_ICON)' -endif -ifneq (,$(MOZ_PKG_MAC_RSRC)) -PKG_DMG_FLAGS += --resource '$(MOZ_PKG_MAC_RSRC)' -endif -ifneq (,$(MOZ_PKG_MAC_EXTRA)) -PKG_DMG_FLAGS += $(MOZ_PKG_MAC_EXTRA) -endif + _ABS_MOZSRCDIR = $(shell cd $(MOZILLA_DIR) && pwd) -ifndef PKG_DMG_SOURCE PKG_DMG_SOURCE = $(STAGEPATH)$(MOZ_PKG_DIR) -endif -INNER_MAKE_PACKAGE = $(_ABS_MOZSRCDIR)/build/package/mac_osx/pkg-dmg \ - --source '$(PKG_DMG_SOURCE)' --target '$(PACKAGE)' \ - --volname '$(MOZ_APP_DISPLAYNAME)' $(PKG_DMG_FLAGS) +INNER_MAKE_PACKAGE = $(call py_action,make_dmg,'$(PKG_DMG_SOURCE)' '$(PACKAGE)') INNER_UNMAKE_PACKAGE = \ set -ex; \ rm -rf $(_ABS_DIST)/unpack.tmp; \ diff --git a/tools/update-packaging/Makefile.in b/tools/update-packaging/Makefile.in index 18041ab894..74d1d85db4 100644 --- a/tools/update-packaging/Makefile.in +++ b/tools/update-packaging/Makefile.in @@ -27,8 +27,8 @@ else PACKAGE_DIR = $(PACKAGE_BASE_DIR)/$(MOZ_PKG_DIR) endif -MAR_BIN = $(LIBXUL_DIST)/host/bin/mar$(HOST_BIN_SUFFIX) -MBSDIFF_BIN = $(LIBXUL_DIST)/host/bin/mbsdiff$(HOST_BIN_SUFFIX) +MAR_BIN = $(DIST)/host/bin/mar$(HOST_BIN_SUFFIX) +MBSDIFF_BIN = $(DIST)/host/bin/mbsdiff$(HOST_BIN_SUFFIX) OVERRIDE_DEFAULT_GOAL := full-update full-update:: complete-patch diff --git a/widget/GfxInfoBase.h b/widget/GfxInfoBase.h index b47fc320de..7f112db47f 100644 --- a/widget/GfxInfoBase.h +++ b/widget/GfxInfoBase.h @@ -49,7 +49,7 @@ public: NS_IMETHOD GetFeatureSuggestedDriverVersion(int32_t aFeature, nsAString & _retval) override; NS_IMETHOD GetWebGLParameter(const nsAString & aParam, nsAString & _retval) override; - NS_IMETHOD GetMonitors(JSContext* cx, JS::MutableHandleValue _retval); + NS_IMETHOD GetMonitors(JSContext* cx, JS::MutableHandleValue _retval) override; NS_IMETHOD GetFailures(uint32_t *failureCount, int32_t** indices, char ***failures) override; NS_IMETHOD_(void) LogFailure(const nsACString &failure) override; NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle) override; diff --git a/widget/MouseEvents.h b/widget/MouseEvents.h index c913af2995..f4056c6701 100644 --- a/widget/MouseEvents.h +++ b/widget/MouseEvents.h @@ -94,7 +94,6 @@ public: virtual WidgetEvent* Duplicate() const override { MOZ_CRASH("WidgetMouseEventBase must not be most-subclass"); - return nullptr; } /// The possible related target diff --git a/widget/TextEvents.h b/widget/TextEvents.h index ef8375e07f..70855cc6fc 100644 --- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -489,7 +489,6 @@ public: NS_ASSERTION(!IsAllowedToDispatchDOMEvent(), "WidgetQueryContentEvent needs to support Duplicate()"); MOZ_CRASH("WidgetQueryContentEvent doesn't support Duplicate()"); - return nullptr; } void InitForQueryTextContent(uint32_t aOffset, uint32_t aLength, diff --git a/widget/WidgetUtils.cpp b/widget/WidgetUtils.cpp index fe15181d02..95bbb8475f 100644 --- a/widget/WidgetUtils.cpp +++ b/widget/WidgetUtils.cpp @@ -92,7 +92,6 @@ nsIntRect RotateRect(nsIntRect aRect, aRect.height, aRect.width); default: MOZ_CRASH("Unknown rotation"); - return aRect; } } diff --git a/widget/gtk/NativeKeyBindings.cpp b/widget/gtk/NativeKeyBindings.cpp index d766ac0eaa..2700ad05fe 100644 --- a/widget/gtk/NativeKeyBindings.cpp +++ b/widget/gtk/NativeKeyBindings.cpp @@ -65,9 +65,25 @@ delete_from_cursor_cb(GtkWidget *w, GtkDeleteType del_type, gint count, gpointer user_data) { g_signal_stop_emission_by_name(w, "delete_from_cursor"); - gHandled = true; - bool forward = count > 0; + +#if (MOZ_WIDGET_GTK == 3) + // Ignore GTK's Ctrl-K keybinding introduced in GTK 3.14 and removed in + // 3.18 if the user has custom bindings set. See bug 1176929. + if (del_type == GTK_DELETE_PARAGRAPH_ENDS && forward && GTK_IS_ENTRY(w) && + !gtk_check_version(3, 14, 1) && gtk_check_version(3, 17, 9)) { + GtkStyleContext* context = gtk_widget_get_style_context(w); + GtkStateFlags flags = gtk_widget_get_state_flags(w); + + GPtrArray* array; + gtk_style_context_get(context, flags, "gtk-key-bindings", &array, nullptr); + if (!array) + return; + g_ptr_array_unref(array); + } +#endif + + gHandled = true; if (uint32_t(del_type) >= ArrayLength(sDeleteCommands)) { // unsupported deletion type return; diff --git a/widget/gtk/gtk2drawing.c b/widget/gtk/gtk2drawing.c index c807751f55..384cab39e4 100644 --- a/widget/gtk/gtk2drawing.c +++ b/widget/gtk/gtk2drawing.c @@ -2748,6 +2748,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, switch (widget) { case MOZ_GTK_BUTTON: + case MOZ_GTK_TOOLBAR_BUTTON: { GtkBorder inner_border; gboolean interior_focus; @@ -3218,6 +3219,7 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, { switch (widget) { case MOZ_GTK_BUTTON: + case MOZ_GTK_TOOLBAR_BUTTON: if (state->depressed) { ensure_toggle_button_widget(); return moz_gtk_button_paint(drawable, rect, cliprect, state, diff --git a/widget/gtk/gtk3drawing.c b/widget/gtk/gtk3drawing.c index c0889aed5c..1179ce15fd 100644 --- a/widget/gtk/gtk3drawing.c +++ b/widget/gtk/gtk3drawing.c @@ -584,10 +584,10 @@ static gint ensure_menu_popup_widget() { if (!gMenuPopupWidget) { - ensure_menu_bar_item_widget(); + ensure_window_widget(); gMenuPopupWidget = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget), - gMenuPopupWidget); + gtk_menu_attach_to_widget(GTK_MENU(gMenuPopupWidget), gProtoWindow, + NULL); gtk_widget_realize(gMenuPopupWidget); } return MOZ_GTK_SUCCESS; @@ -879,6 +879,24 @@ moz_gtk_splitter_get_metrics(gint orientation, gint* size) return MOZ_GTK_SUCCESS; } +static gint +moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect, + GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_window_widget(); + gtk_widget_set_direction(gProtoWindow, direction); + + style = gtk_widget_get_style_context(gProtoWindow); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + static gint moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state, @@ -889,19 +907,8 @@ moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect, GtkStyleContext* style = gtk_widget_get_style_context(widget); gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; - gboolean interior_focus; - gint focus_width, focus_pad; - - moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); gtk_widget_set_direction(widget, direction); - - if (!interior_focus && state->focused) { - x += focus_width + focus_pad; - y += focus_width + focus_pad; - width -= 2 * (focus_width + focus_pad); - height -= 2 * (focus_width + focus_pad); - } - + gtk_style_context_save(style); gtk_style_context_set_state(style, state_flags); @@ -922,9 +929,7 @@ moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect, y += default_top; width -= (default_left + default_right); height -= (default_top + default_bottom); - } - - if (relief != GTK_RELIEF_NONE || state->depressed || + } else if (relief != GTK_RELIEF_NONE || state->depressed || (state_flags & GTK_STATE_FLAG_PRELIGHT)) { /* the following line can trigger an assertion (Crux theme) file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area): @@ -934,20 +939,12 @@ moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect, } if (state->focused) { - if (interior_focus) { - GtkBorder border; - gtk_style_context_get_border(style, state_flags, &border); - x += border.left + focus_pad; - y += border.top + focus_pad; - width -= 2 * (border.left + focus_pad); - height -= 2 * (border.top + focus_pad); - } else { - x -= focus_width + focus_pad; - y -= focus_width + focus_pad; - width += 2 * (focus_width + focus_pad); - height += 2 * (focus_width + focus_pad); - } - + GtkBorder border; + gtk_style_context_get_border(style, state_flags, &border); + x += border.left; + y += border.top; + width -= (border.left + border.right); + height -= (border.top + border.bottom); gtk_render_focus(style, cr, x, y, width, height); } gtk_style_context_restore(style); @@ -960,6 +957,7 @@ moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect, gboolean selected, gboolean inconsistent, gboolean isradio, GtkTextDirection direction) { + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); gint indicator_size, indicator_spacing; gint x, y, width, height; gint focus_x, focus_y, focus_width, focus_height; @@ -996,10 +994,16 @@ moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect, gtk_widget_set_direction(w, direction); gtk_style_context_save(style); + if (selected) + state_flags |= checkbox_check_state; + + if (inconsistent) + state_flags |= GTK_STATE_FLAG_INCONSISTENT; + + gtk_style_context_set_state(style, state_flags); + if (isradio) { gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); - gtk_style_context_set_state(style, selected ? checkbox_check_state : - GTK_STATE_FLAG_NORMAL); gtk_render_option(style, cr, x, y, width, height); if (state->focused) { gtk_render_focus(style, cr, focus_x, focus_y, @@ -1012,14 +1016,7 @@ moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect, * must also be changed for the state to be drawn. */ gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); - if (inconsistent) { - gtk_style_context_set_state(style, GTK_STATE_FLAG_INCONSISTENT); - gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE); - } else if (selected) { - gtk_style_context_set_state(style, checkbox_check_state); - } else { - gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE); - } + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), inconsistent); gtk_render_check(style, cr, x, y, width, height); if (state->focused) { gtk_render_focus(style, cr, @@ -1034,8 +1031,7 @@ moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect, static gint calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, GdkRectangle* inner_rect, - GtkTextDirection direction, - gboolean ignore_focus) + GtkTextDirection direction) { GtkStyleContext* style; GtkBorder border; @@ -1045,8 +1041,7 @@ calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, /* This mirrors gtkbutton's child positioning */ gtk_style_context_get_border(style, 0, &border); - if (!ignore_focus) - gtk_style_context_get_padding(style, 0, &padding); + gtk_style_context_get_padding(style, 0, &padding); inner_rect->x = rect->x + border.left + padding.left; inner_rect->y = rect->y + padding.top + border.top; @@ -1127,16 +1122,30 @@ moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, style = gtk_widget_get_style_context(scrollbar); gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_BUTTON); gtk_style_context_set_state(style, state_flags); + if (arrow_angle == ARROW_RIGHT) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_RIGHT); + } else if (arrow_angle == ARROW_DOWN) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_BOTTOM); + } else if (arrow_angle == ARROW_LEFT) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_LEFT); + } else { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOP); + } gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); arrow_rect.width = rect->width / 2; arrow_rect.height = rect->height / 2; - arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; - arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; + + gfloat arrow_scaling; + gtk_widget_style_get (scrollbar, "arrow-scaling", &arrow_scaling, NULL); + + gdouble arrow_size = MIN(rect->width, rect->height) * arrow_scaling; + arrow_rect.x = rect->x + (rect->width - arrow_size) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_size) / 2; if (state_flags & GTK_STATE_FLAG_ACTIVE) { gtk_widget_style_get(scrollbar, @@ -1151,7 +1160,7 @@ moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, gtk_render_arrow(style, cr, arrow_angle, arrow_rect.x, arrow_rect.y, - arrow_rect.width); + arrow_size); gtk_style_context_restore(style); @@ -1636,7 +1645,7 @@ moz_gtk_treeview_expander_paint(cairo_t *cr, GdkRectangle* rect, static gint moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state, - gboolean ishtml, GtkTextDirection direction) + GtkTextDirection direction) { GdkRectangle arrow_rect, real_arrow_rect; gint arrow_size, separator_width; @@ -1652,7 +1661,7 @@ moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect, gComboBoxButtonWidget, direction); calculate_button_inner_rect(gComboBoxButtonWidget, - rect, &arrow_rect, direction, ishtml); + rect, &arrow_rect, direction); /* Now arrow_rect contains the inner rect ; we want to correct the width * to what the arrow needs (see gtk_combo_box_size_allocate) */ gtk_widget_get_preferred_size(gComboBoxArrowWidget, NULL, &arrow_req); @@ -1762,7 +1771,7 @@ moz_gtk_combo_box_entry_button_paint(cairo_t *cr, GdkRectangle* rect, gComboBoxEntryButtonWidget, direction); calculate_button_inner_rect(gComboBoxEntryButtonWidget, - rect, &arrow_rect, direction, FALSE); + rect, &arrow_rect, direction); if (state_flags & GTK_STATE_FLAG_ACTIVE) { gtk_widget_style_get(gComboBoxEntryButtonWidget, "child-displacement-x", &x_displacement, @@ -1950,14 +1959,25 @@ moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect, GtkStyleContext* style; ensure_frame_widget(); - gtk_widget_set_direction(gStatusbarWidget, direction); + gtk_widget_set_direction(gStatusbarWidget, GTK_TEXT_DIR_LTR); style = gtk_widget_get_style_context(gStatusbarWidget); gtk_style_context_save(style); gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + // Workaround unico not respecting the text direction for resizers. + // See bug 1174248. + cairo_save(cr); + if (direction == GTK_TEXT_DIR_RTL) { + cairo_matrix_t mat; + cairo_matrix_init_translate(&mat, 2 * rect->x + rect->width, 0); + cairo_matrix_scale(&mat, -1, 1); + cairo_transform(cr, &mat); + } + gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height); + cairo_restore(cr); gtk_style_context_restore(style); return MOZ_GTK_SUCCESS; @@ -2012,6 +2032,7 @@ moz_gtk_progress_chunk_paint(cairo_t *cr, GdkRectangle* rect, style = gtk_widget_get_style_context(gProgressWidget); gtk_style_context_save(style); + gtk_style_context_remove_class(style, GTK_STYLE_CLASS_TROUGH); gtk_style_context_add_class(style, GTK_STYLE_CLASS_PROGRESSBAR); if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE || @@ -2047,9 +2068,15 @@ moz_gtk_progress_chunk_paint(cairo_t *cr, GdkRectangle* rect, rect->width = barSize; } } - - gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_render_activity(style, cr, rect->x, rect->y, rect->width, rect->height); + + // gtk_render_activity was used to render progress chunks on GTK versions + // before 3.13.7, see bug 1173907. + if (!gtk_check_version(3, 13, 7)) { + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + } else { + gtk_render_activity(style, cr, rect->x, rect->y, rect->width, rect->height); + } gtk_style_context_restore(style); return MOZ_GTK_SUCCESS; @@ -2406,6 +2433,10 @@ moz_gtk_menu_popup_paint(cairo_t *cr, GdkRectangle* rect, ensure_menu_popup_widget(); gtk_widget_set_direction(gMenuPopupWidget, direction); + // Draw a backing toplevel. This fixes themes that don't provide a menu + // background, and depend on the GtkMenu's implementation window to provide it. + moz_gtk_window_paint(cr, rect, direction); + style = gtk_widget_get_style_context(gMenuPopupWidget); gtk_style_context_save(style); gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENU); @@ -2598,24 +2629,6 @@ moz_gtk_check_menu_item_paint(cairo_t *cr, GdkRectangle* rect, return MOZ_GTK_SUCCESS; } -static gint -moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect, - GtkTextDirection direction) -{ - GtkStyleContext* style; - - ensure_window_widget(); - gtk_widget_set_direction(gProtoWindow, direction); - - style = gtk_widget_get_style_context(gProtoWindow); - gtk_style_context_save(style); - gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); - gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); - gtk_style_context_restore(style); - - return MOZ_GTK_SUCCESS; -} - static gint moz_gtk_info_bar_paint(cairo_t *cr, GdkRectangle* rect, GtkWidgetState* state) @@ -2678,6 +2691,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, switch (widget) { case MOZ_GTK_BUTTON: + case MOZ_GTK_TOOLBAR_BUTTON: { ensure_button_widget(); style = gtk_widget_get_style_context(gButtonWidget); @@ -2687,7 +2701,15 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, /* Don't add this padding in HTML, otherwise the buttons will become too big and stuff the layout. */ if (!inhtml) { + if (widget == MOZ_GTK_TOOLBAR_BUTTON) { + gtk_style_context_save(style); + gtk_style_context_add_class(style, "image-button"); + } + moz_gtk_add_style_padding(style, left, top, right, bottom); + + if (widget == MOZ_GTK_TOOLBAR_BUTTON) + gtk_style_context_restore(style); } moz_gtk_add_style_border(style, left, top, right, bottom); @@ -2697,13 +2719,12 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, { ensure_entry_widget(); style = gtk_widget_get_style_context(gEntryWidget); - moz_gtk_add_style_border(style, left, top, right, bottom); - /* Use the document padding in HTML - and GTK style padding in XUL. */ - if (!inhtml) { - moz_gtk_add_style_padding(style, left, top, right, bottom); - } + // XXX: Subtract 1 pixel from the padding to account for the default + // padding in forms.css. See bug 1187385. + *left = *top = *right = *bottom = -1; + moz_gtk_add_style_padding(style, left, top, right, bottom); + moz_gtk_add_style_border(style, left, top, right, bottom); return MOZ_GTK_SUCCESS; } @@ -2762,10 +2783,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, style = gtk_widget_get_style_context(gComboBoxButtonWidget); - if (!inhtml) { - moz_gtk_add_style_padding(style, left, top, right, bottom); - } - + moz_gtk_add_style_padding(style, left, top, right, bottom); moz_gtk_add_style_border(style, left, top, right, bottom); /* If there is no separator, don't try to count its width. */ @@ -3157,6 +3175,7 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t *cr, switch (widget) { case MOZ_GTK_BUTTON: + case MOZ_GTK_TOOLBAR_BUTTON: if (state->depressed) { ensure_toggle_button_widget(); return moz_gtk_button_paint(cr, rect, state, @@ -3245,8 +3264,7 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t *cr, gEntryWidget, direction); break; case MOZ_GTK_DROPDOWN: - return moz_gtk_combo_box_paint(cr, rect, state, - (gboolean) flags, direction); + return moz_gtk_combo_box_paint(cr, rect, state, direction); break; case MOZ_GTK_DROPDOWN_ARROW: return moz_gtk_combo_box_entry_button_paint(cr, rect, diff --git a/widget/gtk/gtkdrawing.h b/widget/gtk/gtkdrawing.h index 662f03e8ac..9e5c38dc59 100644 --- a/widget/gtk/gtkdrawing.h +++ b/widget/gtk/gtkdrawing.h @@ -89,6 +89,8 @@ typedef gint (*style_prop_t)(GtkStyle*, const gchar*, gint); typedef enum { /* Paints a GtkButton. flags is a GtkReliefStyle. */ MOZ_GTK_BUTTON, + /* Paints a button with image and no text */ + MOZ_GTK_TOOLBAR_BUTTON, /* Paints a GtkCheckButton. flags is a boolean, 1=checked, 0=not checked. */ MOZ_GTK_CHECKBUTTON, /* Paints a GtkRadioButton. flags is a boolean, 1=checked, 0=not checked. */ diff --git a/widget/gtk/mozgtk/mozgtk.c b/widget/gtk/mozgtk/mozgtk.c index 399bcc41ec..e38eab261a 100644 --- a/widget/gtk/mozgtk/mozgtk.c +++ b/widget/gtk/mozgtk/mozgtk.c @@ -264,6 +264,7 @@ STUB(gtk_label_set_markup) STUB(gtk_link_button_new) STUB(gtk_main_do_event) STUB(gtk_main_iteration) +STUB(gtk_menu_attach_to_widget) STUB(gtk_menu_bar_new) STUB(gtk_menu_get_type) STUB(gtk_menu_item_get_type) @@ -530,6 +531,7 @@ STUB(gtk_scale_new) STUB(gtk_scrollbar_new) STUB(gtk_style_context_add_class) STUB(gtk_style_context_add_region) +STUB(gtk_style_context_get) STUB(gtk_style_context_get_background_color) STUB(gtk_style_context_get_border) STUB(gtk_style_context_get_border_color) @@ -538,6 +540,7 @@ STUB(gtk_style_context_get_margin) STUB(gtk_style_context_get_padding) STUB(gtk_style_context_has_class) STUB(gtk_style_context_new) +STUB(gtk_style_context_remove_class) STUB(gtk_style_context_remove_region) STUB(gtk_style_context_restore) STUB(gtk_style_context_save) @@ -545,6 +548,7 @@ STUB(gtk_style_context_set_path) STUB(gtk_style_context_set_state) STUB(gtk_tree_view_column_get_button) STUB(gtk_widget_get_preferred_size) +STUB(gtk_widget_get_state_flags) STUB(gtk_widget_get_style_context) STUB(gtk_widget_path_append_type) STUB(gtk_widget_path_free) diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index bbe660615f..f3f0a2ce63 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -391,11 +391,15 @@ nsNativeThemeGTK::GetGtkWidgetAndState(uint8_t aWidgetType, nsIFrame* aFrame, switch (aWidgetType) { case NS_THEME_BUTTON: + if (aWidgetFlags) + *aWidgetFlags = GTK_RELIEF_NORMAL; + aGtkWidgetType = MOZ_GTK_BUTTON; + break; case NS_THEME_TOOLBAR_BUTTON: case NS_THEME_TOOLBAR_DUAL_BUTTON: if (aWidgetFlags) - *aWidgetFlags = (aWidgetType == NS_THEME_BUTTON) ? GTK_RELIEF_NORMAL : GTK_RELIEF_NONE; - aGtkWidgetType = MOZ_GTK_BUTTON; + *aWidgetFlags = GTK_RELIEF_NONE; + aGtkWidgetType = MOZ_GTK_TOOLBAR_BUTTON; break; case NS_THEME_FOCUS_OUTLINE: aGtkWidgetType = MOZ_GTK_ENTRY; diff --git a/widget/nsNativeTheme.cpp b/widget/nsNativeTheme.cpp index d763d1a115..e8502e4c1e 100644 --- a/widget/nsNativeTheme.cpp +++ b/widget/nsNativeTheme.cpp @@ -46,9 +46,7 @@ nsNativeTheme::GetPresShell(nsIFrame* aFrame) if (!aFrame) return nullptr; - // this is a workaround for the egcs 1.1.2 not inlining - // aFrame->PresContext(), which causes an undefined symbol - nsPresContext *context = aFrame->StyleContext()->RuleNode()->PresContext(); + nsPresContext* context = aFrame->PresContext(); return context ? context->GetPresShell() : nullptr; } diff --git a/widget/tests/TestAppShellSteadyState.cpp b/widget/tests/TestAppShellSteadyState.cpp index 8156335c97..b7e05a976c 100644 --- a/widget/tests/TestAppShellSteadyState.cpp +++ b/widget/tests/TestAppShellSteadyState.cpp @@ -391,8 +391,7 @@ Test4Internal(nsIAppShell* aAppShell) #ifndef XP_WIN // Not sure how to test on other platforms. return false; -#endif - +#else nsCOMPtr appService = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); if (!appService) { @@ -437,6 +436,7 @@ Test4Internal(nsIAppShell* aAppShell) } return true; +#endif } void diff --git a/xulrunner/config/mozconfigs/common.override b/xulrunner/config/mozconfigs/common.override index 2663d3baf5..8d719a5b5e 100644 --- a/xulrunner/config/mozconfigs/common.override +++ b/xulrunner/config/mozconfigs/common.override @@ -5,3 +5,4 @@ # This file is included at the bottom of all xulrunner mozconfigs . "$topsrcdir/build/mozconfig.common.override" +. "$topsrcdir/build/mozconfig.cache" diff --git a/xulrunner/config/mozconfigs/linux32/xulrunner b/xulrunner/config/mozconfigs/linux32/xulrunner index 6f4c10e604..171674f8c8 100644 --- a/xulrunner/config/mozconfigs/linux32/xulrunner +++ b/xulrunner/config/mozconfigs/linux32/xulrunner @@ -6,7 +6,4 @@ ac_add_options --disable-tests . $topsrcdir/build/unix/mozconfig.linux32 -#Use ccache -. "$topsrcdir/build/mozconfig.cache" - . "$topsrcdir/xulrunner/config/mozconfigs/common.override" diff --git a/xulrunner/config/mozconfigs/linux32/xulrunner-qt b/xulrunner/config/mozconfigs/linux32/xulrunner-qt index e04b4c6dac..54e4ecb8f5 100644 --- a/xulrunner/config/mozconfigs/linux32/xulrunner-qt +++ b/xulrunner/config/mozconfigs/linux32/xulrunner-qt @@ -6,12 +6,10 @@ ac_add_options --disable-tests . $topsrcdir/build/unix/mozconfig.linux32 -#Use ccache -. "$topsrcdir/build/mozconfig.cache" - # QT Options export PKG_CONFIG_PATH=/tools/qt-4.6.3/qt/lib/pkgconfig ac_add_options --with-qtdir=/tools/qt-4.6.3/qt ac_add_options --enable-default-toolkit=cairo-qt +ac_add_options --disable-crashreporter . "$topsrcdir/xulrunner/config/mozconfigs/common.override" diff --git a/xulrunner/config/mozconfigs/linux64/xulrunner b/xulrunner/config/mozconfigs/linux64/xulrunner index a0cdabde99..c18a12f679 100644 --- a/xulrunner/config/mozconfigs/linux64/xulrunner +++ b/xulrunner/config/mozconfigs/linux64/xulrunner @@ -6,7 +6,4 @@ ac_add_options --disable-tests . $topsrcdir/build/unix/mozconfig.linux -#Use ccache -. "$topsrcdir/build/mozconfig.cache" - . "$topsrcdir/xulrunner/config/mozconfigs/common.override" diff --git a/xulrunner/installer/Makefile.in b/xulrunner/installer/Makefile.in index cef8ec49d2..3c47632c6f 100644 --- a/xulrunner/installer/Makefile.in +++ b/xulrunner/installer/Makefile.in @@ -96,7 +96,7 @@ GARBAGE += debian/changelog DEBDESTDIR=debian/$(MOZ_BUILD_APP) -GRE_BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build BuildID) +GRE_BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(DIST)/bin/platform.ini Build BuildID) MOZ_DEB_TIMESTAMP = "$(shell date +"%a, %d %b %Y %T %z" )" DEFINES += \ @@ -108,7 +108,7 @@ DEFINES += \ $(NULL) ifeq ($(OS_TARGET),Linux) -debian/changelog: $(srcdir)/debian/changelog.in $(LIBXUL_DIST)/bin/platform.ini +debian/changelog: $(srcdir)/debian/changelog.in $(DIST)/bin/platform.ini $(call py_action,preprocessor, \ $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $^ -o $@)