diff --git a/app/build.gradle b/app/build.gradle index 2753f31..1c926a1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,21 +1,25 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 30 + compileSdk 30 buildToolsVersion '30.0.3' defaultConfig { applicationId 'com.winlator' minSdkVersion 26 targetSdkVersion 28 - versionCode 11 - versionName "5.0" + versionCode 12 + versionName "6.0" } buildTypes { debug { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + + ndk { + abiFilters 'arm64-v8a', 'armeabi-v7a' + } } } @@ -25,7 +29,7 @@ android { externalNativeBuild { cmake { - version '3.10.2' + version '3.22.1' path 'src/main/cpp/CMakeLists.txt' } } diff --git a/app/src/main/assets/.gitattributes b/app/src/main/assets/.gitattributes new file mode 100644 index 0000000..9d74588 --- /dev/null +++ b/app/src/main/assets/.gitattributes @@ -0,0 +1 @@ +*.txz filter=lfs diff=lfs merge=lfs -text diff --git a/app/src/main/assets/box86_64/box64-0.2.2.tzst b/app/src/main/assets/box86_64/box64-0.2.2.tzst deleted file mode 100644 index e3ce4d0..0000000 Binary files a/app/src/main/assets/box86_64/box64-0.2.2.tzst and /dev/null differ diff --git a/app/src/main/assets/box86_64/box64-0.2.6.tzst b/app/src/main/assets/box86_64/box64-0.2.6.tzst index 261dde1..87b5f17 100644 Binary files a/app/src/main/assets/box86_64/box64-0.2.6.tzst and b/app/src/main/assets/box86_64/box64-0.2.6.tzst differ diff --git a/app/src/main/assets/box86_64/box64-0.2.7.tzst b/app/src/main/assets/box86_64/box64-0.2.7.tzst new file mode 100644 index 0000000..4767fcf Binary files /dev/null and b/app/src/main/assets/box86_64/box64-0.2.7.tzst differ diff --git a/app/src/main/assets/box86_64/box86-0.3.0.tzst b/app/src/main/assets/box86_64/box86-0.3.0.tzst index 383fd09..b085219 100644 Binary files a/app/src/main/assets/box86_64/box86-0.3.0.tzst and b/app/src/main/assets/box86_64/box86-0.3.0.tzst differ diff --git a/app/src/main/assets/box86_64/box86-0.3.2.tzst b/app/src/main/assets/box86_64/box86-0.3.2.tzst deleted file mode 100644 index fc62abc..0000000 Binary files a/app/src/main/assets/box86_64/box86-0.3.2.tzst and /dev/null differ diff --git a/app/src/main/assets/box86_64/box86-0.3.5.tzst b/app/src/main/assets/box86_64/box86-0.3.5.tzst new file mode 100644 index 0000000..b8af26e Binary files /dev/null and b/app/src/main/assets/box86_64/box86-0.3.5.tzst differ diff --git a/app/src/main/assets/common_dlls.json b/app/src/main/assets/common_dlls.json new file mode 100644 index 0000000..2fc89ad --- /dev/null +++ b/app/src/main/assets/common_dlls.json @@ -0,0 +1 @@ +{"system32":["winedevice.exe","explorer.exe","iexplore.exe","notepad.exe","msxml.dll","msxml2.dll","msxml3.dll","msxml4.dll","msxml6.dll","shdocvw.dll","acledit.dll","aclui.dll","activeds.dll","activeds.tlb","actxprxy.dll","adsldp.dll","adsldpc.dll","advapi32.dll","advpack.dll","amsi.dll","amstream.dll","apisetschema.dll","apphelp.dll","appwiz.cpl","arp.exe","atl.dll","atl100.dll","atl110.dll","atl80.dll","atl90.dll","atlthunk.dll","atmlib.dll","attrib.exe","authz.dll","avicap32.dll","avifil32.dll","avrt.dll","bcrypt.dll","bluetoothapis.dll","browseui.dll","bthprops.cpl","cabarc.exe","cabinet.dll","cacls.exe","capi2032.dll","cards.dll","cdosys.dll","certutil.exe","cfgmgr32.dll","chcp.com","clock.exe","clusapi.dll","cmd.exe","combase.dll","comcat.dll","comctl32.dll","comdlg32.dll","compstui.dll","comsvcs.dll","concrt140.dll","conhost.exe","connect.dll","control.exe","credui.dll","crtdll.dll","crypt32.dll","cryptdlg.dll","cryptdll.dll","cryptext.dll","cryptnet.dll","cryptowinrt.dll","cryptsp.dll","cryptui.dll","cscript.exe","ctapi32.dll","ctl3d32.dll","d2d1.dll","d3d10.dll","d3d10core.dll","d3d10_1.dll","d3d11.dll","d3d12.dll","d3d8.dll","d3d8thk.dll","d3d9.dll","d3dcompiler_33.dll","d3dcompiler_34.dll","d3dcompiler_35.dll","d3dcompiler_36.dll","d3dcompiler_37.dll","d3dcompiler_38.dll","d3dcompiler_39.dll","d3dcompiler_40.dll","d3dcompiler_41.dll","d3dcompiler_42.dll","d3dcompiler_43.dll","d3dcompiler_46.dll","d3dcompiler_47.dll","d3dim.dll","d3dim700.dll","d3drm.dll","d3dx10_33.dll","d3dx10_34.dll","d3dx10_35.dll","d3dx10_36.dll","d3dx10_37.dll","d3dx10_38.dll","d3dx10_39.dll","d3dx10_40.dll","d3dx10_41.dll","d3dx10_42.dll","d3dx10_43.dll","d3dx11_42.dll","d3dx11_43.dll","d3dx9_24.dll","d3dx9_25.dll","d3dx9_26.dll","d3dx9_27.dll","d3dx9_28.dll","d3dx9_29.dll","d3dx9_30.dll","d3dx9_31.dll","d3dx9_32.dll","d3dx9_33.dll","d3dx9_34.dll","d3dx9_35.dll","d3dx9_36.dll","d3dx9_37.dll","d3dx9_38.dll","d3dx9_39.dll","d3dx9_40.dll","d3dx9_41.dll","d3dx9_42.dll","d3dx9_43.dll","d3dxof.dll","davclnt.dll","dbgeng.dll","dbghelp.dll","dciman32.dll","dcomp.dll","ddraw.dll","ddrawex.dll","devenum.dll","dhcpcsvc.dll","dhcpcsvc6.dll","dhtmled.ocx","difxapi.dll","dinput.dll","dinput8.dll","directmanipulation.dll","dism.exe","dispex.dll","dllhost.exe","dmband.dll","dmcompos.dll","dmime.dll","dmloader.dll","dmscript.dll","dmstyle.dll","dmsynth.dll","dmusic.dll","dmusic32.dll","dnsapi.dll","dplay.dll","dplaysvr.exe","dplayx.dll","dpnaddr.dll","dpnet.dll","dpnhpast.dll","dpnhupnp.dll","dpnlobby.dll","dpnsvr.exe","dpvoice.dll","dpvsetup.exe","dpwsockx.dll","drmclien.dll","dsdmo.dll","dsound.dll","dsquery.dll","dssenh.dll","dsuiext.dll","dswave.dll","dwmapi.dll","dwrite.dll","dx8vb.dll","dxdiag.exe","dxdiagn.dll","dxgi.dll","esent.dll","expand.exe","fc.exe","glu32.dll","hnetcfg.dll","icmp.dll","ieproxy.dll","initpki.dll","inkobj.dll","joy.cpl","jsproxy.dll","kerberos.dll","lz32.dll","mciavi32.dll","mf.dll","mfmediaengine.dll","mfreadwrite.dll","mgmtapi.dll","mlang.dll","mprapi.dll","msasn1.dll","msctf.dll","msgsm32.acm","msimtf.dll","msinfo32.exe","mspatcha.dll","msrle32.dll","msv1_0.dll","msvcp140.dll","msvcp140_1.dll","msvcr71.dll","msvcrt20.dll","msvcrtd.dll","msvfw32.dll","net.exe","ntdsapi.dll","odbc32.dll","ole32.dll","oleacc.dll","oleview.exe","opengl32.dll","pdh.dll","prntvpt.dll","psapi.dll","qasf.dll","reg.exe","regini.exe","rpcrt4.dll","rpcss.exe","rsaenh.dll","sc.exe","scrobj.dll","scrrun.dll","secur32.dll","sensapi.dll","serialui.dll","sfc_os.dll","slbcsp.dll","srvcli.dll","systeminfo.exe","tbs.dll","tdh.dll","ucrtbase.dll","uianimation.dll","uiribbon.dll","vcomp100.dll","vcomp140.dll","version.dll","wdscore.dll","websocket.dll","wevtapi.dll","win32u.dll","windows.networking.dll","wineboot.exe","winecfg.exe","winedbg.exe","winefile.exe","winemapi.dll","winemenubuilder.exe","winmm.dll","winusb.dll","winver.exe","wlanapi.dll","wlanui.dll","wmasf.dll","wow64.dll","wow64win.dll","wpcap.dll","wsdapi.dll","wshom.ocx","wsnmp32.dll","wuapi.dll","x3daudio1_0.dll","x3daudio1_4.dll","x3daudio1_6.dll","xactengine2_0.dll","xactengine2_4.dll","xactengine2_9.dll","xapofx1_2.dll","dxtrans.dll","dxva2.dll","explorerframe.dll","extrac32.exe","findstr.exe","fwpuclnt.dll","gamingtcui.dll","gpkcsp.dll","hhctrl.ocx","hlink.dll","ieframe.dll","imaadp32.acm","imagehlp.dll","ipconfig.exe","iphlpapi.dll","iprop.dll","irprops.cpl","kernel32.dll","localspl.dll","mapistub.dll","mcicda.dll","mciqtz32.dll","mciseq.dll","mferror.dll","mpr.dll","msctfmonitor.dll","msdelta.dll","msg711.acm","msident.dll","msisip.dll","msports.dll","msscript.ocx","mssip32.dll","msvcm80.dll","msvcp140_2.dll","msvcp140_atomic_wait.dll","msvcp60.dll","msvcp90.dll","msvcp_win.dll","msvcr100.dll","msvcr80.dll","msvcr90.dll","msvidc32.dll","mswsock.dll","ncrypt.dll","netprofm.dll","netstat.exe","normaliz.dll","ntprint.dll","odbcbcp.dll","odbccp32.dll","oleaut32.dll","olesvr32.dll","pidgen.dll","ping.exe","printui.dll","qcap.dll","qmgrprxy.dll","qwave.dll","rasapi32.dll","regapi.dll","riched20.dll","robocopy.exe","rsabase.dll","rtworkq.dll","sas.dll","scarddlg.dll","sdbinst.exe","secedit.exe","shdoclc.dll","shell32.dll","shlwapi.dll","shutdown.exe","start.exe","strmdll.dll","tapi32.dll","taskmgr.exe","taskschd.dll","twain_32.dll","unicows.dll","urlmon.dll","usp10.dll","utildll.dll","vbscript.dll","vcomp110.dll","vcomp120.dll","vcruntime140.dll","virtdisk.dll","wiaservc.dll","windows.globalization.dll","windows.media.devices.dll","windowscodecsext.dll","wined3d.dll","winepath.exe","xactengine3_1.dll","xactengine3_4.dll","xactengine3_6.dll","xapofx1_3.dll","xaudio2_5.dll","xcopy.exe","xinput1_4.dll","eject.exe","evr.dll","faultrep.dll","find.exe","fntcache.dll","fontsub.dll","hid.dll","hostname.exe","httpapi.dll","icacls.exe","iccvid.dll","icinfo.exe","imm32.dll","loadperf.dll","localui.dll","mapi32.dll","mf3216.dll","midimap.dll","msacm32.dll","mscms.dll","mscoree.dll","msdmo.dll","msdrm.dll","msftedit.dll","mshtml.tlb","msi.dll","msidb.exe","msiexec.exe","msls31.dll","msnet32.dll","msvcm90.dll","msvcp120_app.dll","msvcp70.dll","msvcp71.dll","msvcr110.dll","msvcr120.dll","msvcr120_app.dll","msvcrt40.dll","mtxdm.dll","nddeapi.dll","netapi32.dll","netutils.dll","newdev.dll","ninput.dll","ntdll.dll","ntoskrnl.exe","odbccu32.dll","olecli32.dll","oledlg.dll","olethk32.dll","plugplay.exe","propsys.dll","qedit.dll","rasdlg.dll","regsvr32.exe","rtutils.dll","samlib.dll","schannel.dll","schedsvc.dll","sechost.dll","setupapi.dll","setx.exe","sfc.dll","slc.dll","spoolss.dll","sppc.dll","sspicli.dll","sti.dll","svrapi.dll","sxs.dll","taskkill.exe","tasklist.exe","uninstaller.exe","updspapi.dll","url.dll","user32.dll","userenv.dll","vcomp.dll","vcruntime140_1.dll","vdmdbg.dll","webservices.dll","wevtsvc.dll","wevtutil.exe","wimgapi.dll","windows.gaming.input.dll","windows.media.dll","windowscodecs.dll","winebrowser.exe","wineconsole.exe","winemsibuilder.exe","winepulse.drv","winex11.drv","winhttp.dll","winnls32.dll","wintrust.dll","wmi.dll","wmvcore.dll","wow64cpu.dll","write.exe","wscript.exe","wsock32.dll","x3daudio1_3.dll","xactengine3_0.dll","feclient.dll","fltlib.dll","fsutil.exe","gameux.dll","gdi32.dll","gdiplus.dll","hal.dll","ia2comproxy.dll","inetcomm.dll","inetcpl.cpl","inetmib1.dll","infosoft.dll","inseng.dll","itircl.dll","itss.dll","jscript.dll","kernelbase.dll","ksproxy.ax","ksuser.dll","ktmw32.dll","l3codeca.acm","lodctr.exe","mciwave.dll","mfplat.dll","mfplay.dll","mfsrcsnk.dll","mmcndmgr.dll","mmdevapi.dll","mp3dmod.dll","msacm32.drv","msadp32.acm","mscat32.dll","msctfp.dll","mshta.exe","mshtml.dll","msimg32.dll","msimsg.dll","msisys.ocx","mssign32.dll","mstask.dll","msvcirt.dll","msvcp100.dll","msvcp110.dll","msvcp120.dll","msvcp80.dll","msvcr70.dll","msvcrt.dll","netcfgx.dll","netsh.exe","npptools.dll","nsi.dll","objsel.dll","olepro32.dll","opcservices.dll","opencl.dll","packager.dll","photometadatahandler.dll","powrprof.dll","progman.exe","pstorec.dll","pwrshplugin.dll","qdvd.dll","qmgr.dll","quartz.dll","query.dll","resutils.dll","riched32.dll","rstrtmgr.dll","rundll32.exe","sccbase.dll","schtasks.exe","security.dll","services.exe","shcore.dll","shfolder.dll","snmpapi.dll","softpub.dll","spoolsv.exe","srclient.dll","stdole2.tlb","stdole32.tlb","subst.exe","svchost.exe","t2embed.dll","termsv.exe","threadpoolwinrt.dll","traffic.dll","tzres.dll","uiautomationcore.dll","unlodctr.exe","uxtheme.dll","vcomp90.dll","vga.dll","view.exe","vssapi.dll","vulkan-1.dll","wer.dll","where.exe","whoami.exe","windows.devices.enumeration.dll","winealsa.drv","winspool.drv","wmp.dll","ws2_32.dll","xaudio2_7.dll","wintypes.dll","wofutil.dll","wuauserv.exe","x3daudio1_1.dll","xactengine3_5.dll","xaudio2_3.dll","windows.gaming.ui.gamebar.dll","windows.media.speech.dll","winegstreamer.dll","winemine.exe","winevulkan.dll","wing32.dll","winhlp32.exe","xapofx1_5.dll","xaudio2_1.dll","xaudio2_2.dll","xaudio2_4.dll","xaudio2_9.dll","wnaspi32.dll","x3daudio1_5.dll","xinput1_2.dll","xmllite.dll","wldap32.dll","wmphoto.dll","wpc.dll","x3daudio1_7.dll","xaudio2_0.dll","wuaueng.dll","wusa.exe","x3daudio1_2.dll","xinput9_1_0.dll","xinputuap.dll","xpsprint.dll","xpssvcs.dll","xactengine3_3.dll","wtsapi32.dll","xactengine3_7.dll","xolehlp.dll","xaudio2_6.dll","xaudio2_8.dll","xinput1_1.dll","wininet.dll","winmgmt.exe","winscard.dll","winsta.dll","wintab32.dll","xactengine2_7.dll","xactengine3_2.dll","xapofx1_4.dll","xapofx1_1.dll","xinput1_3.dll"],"syswow64":["start.exe","winhlp32.exe","stdole2.tlb","iexplore.exe","msxml.dll","msxml2.dll","msxml3.dll","msxml4.dll","msxml6.dll","shdocvw.dll","acledit.dll","aclui.dll","activeds.dll","activeds.tlb","actxprxy.dll","adsldp.dll","adsldpc.dll","advapi32.dll","advpack.dll","amsi.dll","amstream.dll","apisetschema.dll","apphelp.dll","appwiz.cpl","arp.exe","atl.dll","atl100.dll","atl110.dll","atl80.dll","atl90.dll","atlthunk.dll","atmlib.dll","attrib.exe","authz.dll","avicap32.dll","avifil32.dll","avrt.dll","bcrypt.dll","bluetoothapis.dll","browseui.dll","bthprops.cpl","cabarc.exe","cabinet.dll","cacls.exe","capi2032.dll","cards.dll","cdosys.dll","certutil.exe","cfgmgr32.dll","chcp.com","clock.exe","clusapi.dll","cmd.exe","combase.dll","comcat.dll","comctl32.dll","comdlg32.dll","compstui.dll","comsvcs.dll","concrt140.dll","connect.dll","control.exe","credui.dll","crtdll.dll","crypt32.dll","cryptdlg.dll","cryptdll.dll","cryptext.dll","cryptnet.dll","cryptowinrt.dll","cryptsp.dll","cryptui.dll","cscript.exe","ctapi32.dll","ctl3d32.dll","d2d1.dll","d3d10.dll","d3d10core.dll","d3d10_1.dll","d3d11.dll","d3d12.dll","d3d8.dll","d3d8thk.dll","d3d9.dll","d3dcompiler_33.dll","d3dcompiler_34.dll","d3dcompiler_35.dll","d3dcompiler_36.dll","d3dcompiler_37.dll","d3dcompiler_38.dll","d3dcompiler_39.dll","d3dcompiler_40.dll","d3dcompiler_41.dll","d3dcompiler_42.dll","d3dcompiler_43.dll","d3dcompiler_46.dll","d3dcompiler_47.dll","d3dim.dll","d3dim700.dll","d3drm.dll","d3dx10_33.dll","d3dx10_34.dll","d3dx10_35.dll","d3dx10_36.dll","d3dx10_37.dll","d3dx10_38.dll","d3dx10_39.dll","d3dx10_40.dll","d3dx10_41.dll","d3dx10_42.dll","d3dx10_43.dll","d3dx11_42.dll","d3dx11_43.dll","d3dx9_24.dll","d3dx9_25.dll","d3dx9_26.dll","d3dx9_27.dll","d3dx9_28.dll","d3dx9_29.dll","d3dx9_30.dll","d3dx9_31.dll","d3dx9_32.dll","d3dx9_33.dll","d3dx9_34.dll","d3dx9_35.dll","d3dx9_36.dll","d3dx9_37.dll","d3dx9_38.dll","d3dx9_39.dll","d3dx9_40.dll","d3dx9_41.dll","d3dx9_42.dll","d3dx9_43.dll","d3dxof.dll","davclnt.dll","dbgeng.dll","dbghelp.dll","dciman32.dll","dcomp.dll","ddraw.dll","ddrawex.dll","devenum.dll","dhcpcsvc.dll","dhcpcsvc6.dll","dhtmled.ocx","difxapi.dll","dinput.dll","dinput8.dll","directmanipulation.dll","dism.exe","dispex.dll","dllhost.exe","dmband.dll","dmcompos.dll","dmime.dll","dmloader.dll","dmscript.dll","dmstyle.dll","dmsynth.dll","dmusic.dll","dmusic32.dll","dnsapi.dll","dplay.dll","dplaysvr.exe","dplayx.dll","dpnaddr.dll","dpnet.dll","dpnhpast.dll","dpnhupnp.dll","dpnlobby.dll","dpnsvr.exe","dpvoice.dll","dpvsetup.exe","dpwsockx.dll","drmclien.dll","dsdmo.dll","dsound.dll","dsquery.dll","dssenh.dll","dsuiext.dll","dswave.dll","dwmapi.dll","dwrite.dll","dx8vb.dll","dxdiag.exe","dxdiagn.dll","dxgi.dll","dxtrans.dll","dxva2.dll","eject.exe","esent.dll","explorerframe.dll","fwpuclnt.dll","gameux.dll","gdi32.dll","gdiplus.dll","gpkcsp.dll","hid.dll","hlink.dll","httpapi.dll","imaadp32.acm","imm32.dll","inetcpl.cpl","inetmib1.dll","inkobj.dll","jsproxy.dll","kernel32.dll","localui.dll","lodctr.exe","lz32.dll","mapistub.dll","mciwave.dll","mferror.dll","mfplay.dll","mmdevapi.dll","mp3dmod.dll","mpr.dll","msacm32.dll","msctfmonitor.dll","msctfp.dll","msdelta.dll","msg711.acm","msi.dll","msident.dll","msimg32.dll","msinfo32.exe","mspatcha.dll","msscript.ocx","msvcp100.dll","msvcp140.dll","msvcp60.dll","msvcp70.dll","msvcp_win.dll","msvcr120.dll","msvcrt.dll","msvcrt20.dll","msvcrtd.dll","netapi32.dll","newdev.dll","ntdsapi.dll","odbc32.dll","oleaut32.dll","opengl32.dll","pidgen.dll","ping.exe","progman.exe","psapi.dll","qdvd.dll","qmgr.dll","regapi.dll","rsabase.dll","rtutils.dll","sc.exe","schedsvc.dll","scrobj.dll","sdbinst.exe","secedit.exe","setupapi.dll","shcore.dll","shdoclc.dll","shutdown.exe","slc.dll","softpub.dll","stdole32.tlb","sti.dll","svrapi.dll","tapi32.dll","taskschd.dll","uianimation.dll","uiautomationcore.dll","unicows.dll","uninstaller.exe","unlodctr.exe","updspapi.dll","userenv.dll","uxtheme.dll","vcomp120.dll","vcomp90.dll","vga.dll","webservices.dll","wevtsvc.dll","where.exe","wiaservc.dll","windows.gaming.ui.gamebar.dll","windows.media.devices.dll","windows.media.speech.dll","winealsa.drv","winebrowser.exe","wineconsole.exe","wined3d.dll","winemapi.dll","winmgmt.exe","winmm.dll","winscard.dll","winspool.drv","wintab32.dll","winver.exe","expand.exe","explorer.exe","extrac32.exe","fc.exe","feclient.dll","fsutil.exe","hal.dll","hhctrl.ocx","icmp.dll","ieframe.dll","ieproxy.dll","inetcomm.dll","inseng.dll","ipconfig.exe","iprop.dll","kernelbase.dll","ktmw32.dll","localspl.dll","mcicda.dll","mlang.dll","mmcndmgr.dll","mscms.dll","msdmo.dll","msftedit.dll","mshtml.tlb","msiexec.exe","msimsg.dll","msisys.ocx","msnet32.dll","msports.dll","mssip32.dll","msvcp110.dll","msvcp140_1.dll","msvcp140_2.dll","msvcp80.dll","msvcr100.dll","msvcr110.dll","msvcr71.dll","msvcr80.dll","net.exe","netprofm.dll","netsh.exe","netutils.dll","ninput.dll","npptools.dll","nsi.dll","ntoskrnl.exe","objsel.dll","odbcbcp.dll","ole32.dll","olesvr32.dll","olethk32.dll","pdh.dll","propsys.dll","pstorec.dll","query.dll","rasapi32.dll","riched32.dll","robocopy.exe","rpcrt4.dll","rpcss.exe","rstrtmgr.dll","scarddlg.dll","sccbase.dll","shfolder.dll","slbcsp.dll","srvcli.dll","sspicli.dll","strmdll.dll","subst.exe","taskmgr.exe","termsv.exe","tzres.dll","utildll.dll","vcomp.dll","vcomp100.dll","vcomp110.dll","vdmdbg.dll","version.dll","virtdisk.dll","vulkan-1.dll","wevtutil.exe","wimgapi.dll","win32u.dll","windows.gaming.input.dll","windows.media.dll","windowscodecs.dll","winedbg.exe","winedevice.exe","winefile.exe","winex11.drv","winhttp.dll","wmasf.dll","wmvcore.dll","write.exe","wscript.exe","wsnmp32.dll","wusa.exe","evr.dll","findstr.exe","fltlib.dll","hnetcfg.dll","hostname.exe","ia2comproxy.dll","icacls.exe","iccvid.dll","icinfo.exe","initpki.dll","itircl.dll","itss.dll","joy.cpl","jscript.dll","ksuser.dll","mapi32.dll","mciavi32.dll","mciqtz32.dll","mgmtapi.dll","midimap.dll","mprapi.dll","msacm32.drv","msadp32.acm","msasn1.dll","mscat32.dll","msdrm.dll","msgsm32.acm","mshta.exe","msimtf.dll","msls31.dll","mstask.dll","msvcirt.dll","msvcm80.dll","msvcm90.dll","msvcp90.dll","msvcr90.dll","msvcrt40.dll","msvfw32.dll","msvidc32.dll","mswsock.dll","nddeapi.dll","netcfgx.dll","netstat.exe","odbccp32.dll","odbccu32.dll","oleacc.dll","olecli32.dll","oledlg.dll","oleview.exe","opcservices.dll","opencl.dll","packager.dll","prntvpt.dll","qedit.dll","qmgrprxy.dll","qwave.dll","reg.exe","regedit.exe","regsvr32.exe","resutils.dll","riched20.dll","rsaenh.dll","rundll32.exe","samlib.dll","sas.dll","schtasks.exe","scrrun.dll","sechost.dll","secur32.dll","security.dll","sensapi.dll","serialui.dll","shlwapi.dll","sppc.dll","srclient.dll","svchost.exe","t2embed.dll","taskkill.exe","threadpoolwinrt.dll","ucrtbase.dll","uiribbon.dll","usp10.dll","vbscript.dll","vcomp140.dll","vcruntime140.dll","view.exe","vssapi.dll","wer.dll","whoami.exe","windows.devices.enumeration.dll","windows.globalization.dll","wineboot.exe","winecfg.exe","winemsibuilder.exe","wing32.dll","wintypes.dll","winusb.dll","faultrep.dll","find.exe","fntcache.dll","fontsub.dll","gamingtcui.dll","glu32.dll","hh.exe","imagehlp.dll","infosoft.dll","iphlpapi.dll","irprops.cpl","kerberos.dll","ksproxy.ax","l3codeca.acm","loadperf.dll","mciseq.dll","mf.dll","mf3216.dll","mfmediaengine.dll","mfplat.dll","mfreadwrite.dll","mfsrcsnk.dll","mscoree.dll","msctf.dll","mshtml.dll","msidb.exe","msisip.dll","msrle32.dll","mssign32.dll","msv1_0.dll","msvcp120.dll","msvcp120_app.dll","msvcp140_atomic_wait.dll","msvcp71.dll","msvcr120_app.dll","msvcr70.dll","mtxdm.dll","ncrypt.dll","normaliz.dll","notepad.exe","ntdll.dll","ntprint.dll","olepro32.dll","photometadatahandler.dll","plugplay.exe","powrprof.dll","printui.dll","pwrshplugin.dll","qasf.dll","qcap.dll","quartz.dll","rasdlg.dll","regini.exe","rtworkq.dll","schannel.dll","setx.exe","sfc.dll","sfc_os.dll","shell32.dll","snmpapi.dll","spoolss.dll","sxs.dll","systeminfo.exe","tasklist.exe","tbs.dll","tdh.dll","traffic.dll","url.dll","urlmon.dll","user32.dll","vcruntime140_1.dll","wdscore.dll","websocket.dll","wevtapi.dll","windows.networking.dll","windowscodecsext.dll","winegstreamer.dll","winemine.exe","winepath.exe","winepulse.drv","winevulkan.dll","wininet.dll","winnls32.dll","winsta.dll","wintrust.dll","wlanui.dll","wmi.dll","wmphoto.dll","wnaspi32.dll","wpc.dll","wpcap.dll","x3daudio1_0.dll","x3daudio1_1.dll","x3daudio1_3.dll","xactengine3_1.dll","xactengine3_3.dll","wlanapi.dll","ws2_32.dll","wshom.ocx","wuauserv.exe","xactengine3_7.dll","xapofx1_2.dll","xaudio2_4.dll","x3daudio1_2.dll","x3daudio1_6.dll","xactengine2_0.dll","x3daudio1_4.dll","x3daudio1_7.dll","xactengine3_0.dll","xaudio2_6.dll","xactengine3_4.dll","xapofx1_3.dll","xaudio2_5.dll","xapofx1_5.dll","xaudio2_1.dll","xinput1_4.dll","xpsprint.dll","xinput1_1.dll","xaudio2_8.dll","wsock32.dll","xactengine3_6.dll","xaudio2_2.dll","xaudio2_0.dll","xaudio2_3.dll","xaudio2_7.dll","xolehlp.dll","xpssvcs.dll","xactengine2_4.dll","xactengine2_7.dll","wldap32.dll","wmp.dll","wuapi.dll","wuaueng.dll","xactengine3_2.dll","xaudio2_9.dll","xinput1_2.dll","wofutil.dll","x3daudio1_5.dll","xactengine2_9.dll","xinput9_1_0.dll","xcopy.exe","xinputuap.dll","xapofx1_4.dll","xinput1_3.dll","wsdapi.dll","wtsapi32.dll","xactengine3_5.dll","xapofx1_1.dll","xmllite.dll"]} \ No newline at end of file diff --git a/app/src/main/assets/container_pattern.tzst b/app/src/main/assets/container_pattern.tzst new file mode 100644 index 0000000..1d63854 Binary files /dev/null and b/app/src/main/assets/container_pattern.tzst differ diff --git a/app/src/main/assets/dxwrapper/d8vk-1.0.tzst b/app/src/main/assets/dxwrapper/d8vk.tzst similarity index 100% rename from app/src/main/assets/dxwrapper/d8vk-1.0.tzst rename to app/src/main/assets/dxwrapper/d8vk.tzst diff --git a/app/src/main/assets/dxwrapper/dxvk-2.2.tzst b/app/src/main/assets/dxwrapper/dxvk-2.2.tzst deleted file mode 100644 index b1b9be7..0000000 Binary files a/app/src/main/assets/dxwrapper/dxvk-2.2.tzst and /dev/null differ diff --git a/app/src/main/assets/dxwrapper/dxvk-2.3.1.tzst b/app/src/main/assets/dxwrapper/dxvk-2.3.1.tzst new file mode 100644 index 0000000..7b76e70 Binary files /dev/null and b/app/src/main/assets/dxwrapper/dxvk-2.3.1.tzst differ diff --git a/app/src/main/assets/dxwrapper/wined3d-7.8.tzst b/app/src/main/assets/dxwrapper/wined3d-7.8.tzst deleted file mode 100644 index 5f44286..0000000 Binary files a/app/src/main/assets/dxwrapper/wined3d-7.8.tzst and /dev/null differ diff --git a/app/src/main/assets/dxwrapper/wined3d-8.14.tzst b/app/src/main/assets/dxwrapper/wined3d-8.14.tzst deleted file mode 100644 index 91e5c34..0000000 Binary files a/app/src/main/assets/dxwrapper/wined3d-8.14.tzst and /dev/null differ diff --git a/app/src/main/assets/graphics_driver/llvmpipe-23.1.6.tzst b/app/src/main/assets/graphics_driver/llvmpipe-23.1.6.tzst deleted file mode 100644 index a24b863..0000000 Binary files a/app/src/main/assets/graphics_driver/llvmpipe-23.1.6.tzst and /dev/null differ diff --git a/app/src/main/assets/graphics_driver/turnip-23.1.6.tzst b/app/src/main/assets/graphics_driver/turnip-23.1.6.tzst deleted file mode 100644 index 79c5320..0000000 Binary files a/app/src/main/assets/graphics_driver/turnip-23.1.6.tzst and /dev/null differ diff --git a/app/src/main/assets/graphics_driver/turnip-23.3.0.tzst b/app/src/main/assets/graphics_driver/turnip-23.3.0.tzst deleted file mode 100644 index 5e518e9..0000000 Binary files a/app/src/main/assets/graphics_driver/turnip-23.3.0.tzst and /dev/null differ diff --git a/app/src/main/assets/graphics_driver/turnip-24.0.0.tzst b/app/src/main/assets/graphics_driver/turnip-24.0.0.tzst deleted file mode 100644 index 0ad6a0d..0000000 Binary files a/app/src/main/assets/graphics_driver/turnip-24.0.0.tzst and /dev/null differ diff --git a/app/src/main/assets/graphics_driver/turnip-24.0.2.tzst b/app/src/main/assets/graphics_driver/turnip-24.0.2.tzst new file mode 100644 index 0000000..73d3e70 Binary files /dev/null and b/app/src/main/assets/graphics_driver/turnip-24.0.2.tzst differ diff --git a/app/src/main/assets/graphics_driver/virgl-22.1.7.tzst b/app/src/main/assets/graphics_driver/virgl-22.1.7.tzst index eab9709..a2f4c9e 100644 Binary files a/app/src/main/assets/graphics_driver/virgl-22.1.7.tzst and b/app/src/main/assets/graphics_driver/virgl-22.1.7.tzst differ diff --git a/app/src/main/assets/graphics_driver/zink-22.2.2.tzst b/app/src/main/assets/graphics_driver/zink-22.2.2.tzst index ff61d4d..da1ecf5 100644 Binary files a/app/src/main/assets/graphics_driver/zink-22.2.2.tzst and b/app/src/main/assets/graphics_driver/zink-22.2.2.tzst differ diff --git a/app/src/main/assets/imagefs.txz b/app/src/main/assets/imagefs.txz new file mode 100644 index 0000000..da099a4 --- /dev/null +++ b/app/src/main/assets/imagefs.txz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffbd6b8bb2d8df316038c1ffbe6a882b620ac5a87c078e3272d8299d79de1b64 +size 167540620 diff --git a/app/src/main/assets/imagefs_patches.tzst b/app/src/main/assets/imagefs_patches.tzst new file mode 100644 index 0000000..1af52ea Binary files /dev/null and b/app/src/main/assets/imagefs_patches.tzst differ diff --git a/app/src/main/assets/patches.tzst b/app/src/main/assets/patches.tzst deleted file mode 100644 index 72931ec..0000000 Binary files a/app/src/main/assets/patches.tzst and /dev/null differ diff --git a/app/src/main/assets/wincomponents/direct3d.tzst b/app/src/main/assets/wincomponents/direct3d.tzst new file mode 100644 index 0000000..ffdeebb Binary files /dev/null and b/app/src/main/assets/wincomponents/direct3d.tzst differ diff --git a/app/src/main/assets/wincomponents/directmusic.tzst b/app/src/main/assets/wincomponents/directmusic.tzst new file mode 100644 index 0000000..237ba7a Binary files /dev/null and b/app/src/main/assets/wincomponents/directmusic.tzst differ diff --git a/app/src/main/assets/wincomponents/directplay.tzst b/app/src/main/assets/wincomponents/directplay.tzst new file mode 100644 index 0000000..c263e89 Binary files /dev/null and b/app/src/main/assets/wincomponents/directplay.tzst differ diff --git a/app/src/main/assets/wincomponents/directshow.tzst b/app/src/main/assets/wincomponents/directshow.tzst new file mode 100644 index 0000000..9f6e129 Binary files /dev/null and b/app/src/main/assets/wincomponents/directshow.tzst differ diff --git a/app/src/main/assets/wincomponents/directsound.tzst b/app/src/main/assets/wincomponents/directsound.tzst new file mode 100644 index 0000000..791a16c Binary files /dev/null and b/app/src/main/assets/wincomponents/directsound.tzst differ diff --git a/app/src/main/assets/wincomponents/vcrun2010.tzst b/app/src/main/assets/wincomponents/vcrun2010.tzst new file mode 100644 index 0000000..0c29cb7 Binary files /dev/null and b/app/src/main/assets/wincomponents/vcrun2010.tzst differ diff --git a/app/src/main/assets/dxcomponents.json b/app/src/main/assets/wincomponents/wincomponents.json similarity index 89% rename from app/src/main/assets/dxcomponents.json rename to app/src/main/assets/wincomponents/wincomponents.json index cf11451..94d236e 100644 --- a/app/src/main/assets/dxcomponents.json +++ b/app/src/main/assets/wincomponents/wincomponents.json @@ -3,5 +3,6 @@ "directsound" : ["dsound"], "directmusic" : ["dmband", "dmcompos", "dmime", "dmloader", "dmscript", "dmstyle", "dmsynth", "dmusic", "dmusic32", "dswave"], "directshow" : ["amstream", "qasf", "qcap", "qdvd", "qedit", "quartz"], - "directplay" : ["dplaysvr.exe", "dplayx", "dpmodemx", "dpnet", "dpnhpast", "dpnhupnp", "dpnsvr.exe", "dpwsockx"] + "directplay" : ["dplaysvr.exe", "dplayx", "dpmodemx", "dpnet", "dpnhpast", "dpnhupnp", "dpnsvr.exe", "dpwsockx"], + "vcrun2010" : ["msvcp100", "msvcr100", "vcomp100", "atl100"] } \ No newline at end of file diff --git a/app/src/main/assets/wine_startmenu.json b/app/src/main/assets/wine_startmenu.json index 852e8f3..934e96d 100644 --- a/app/src/main/assets/wine_startmenu.json +++ b/app/src/main/assets/wine_startmenu.json @@ -2,6 +2,10 @@ { "name" : "Programs", "children" : [ + { + "name" : "7-Zip", + "path" : "Z:/opt/apps/7-Zip/7zFM.exe" + }, { "name" : "Internet Explorer", "path" : "C:/windows/system32/iexplore.exe" @@ -12,7 +16,8 @@ }, { "name" : "Wordpad", - "path" : "C:/windows/system32/write.exe" + "path" : "C:/windows/system32/write.exe", + "iconLocation" : "Z:/opt/wine/lib/wine/i386-windows/wordpad.exe" }, { "name" : "Games", @@ -46,7 +51,10 @@ }, { "name" : "Wine Mono Installer", - "path" : "Z:/opt/resources/winemono.bat" + "path" : "C:/windows/system32/wineboot.exe", + "cmdArgs" : "-u", + "iconLocation" : "shell32.dll", + "iconIndex" : -25 }, { "name" : "Wine Configuration", diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 75bc5c6..f5da032 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -1,6 +1,9 @@ -cmake_minimum_required(VERSION 3.10.2) +cmake_minimum_required(VERSION 3.22.1) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-unused-function") +set(CMAKE_C_FLAGS "-O2 -Wno-unused-function -Wimplicit-function-declaration") + +add_subdirectory(proot) +add_subdirectory(virglrenderer) add_library(winlator SHARED drawable.c diff --git a/app/src/main/cpp/drawable.c b/app/src/main/cpp/drawable.c index 2f88902..e069c1e 100644 --- a/app/src/main/cpp/drawable.c +++ b/app/src/main/cpp/drawable.c @@ -108,10 +108,10 @@ Java_com_winlator_xserver_Drawable_copyArea(JNIEnv *env, jclass obj, jshort srcX JNIEXPORT void JNICALL Java_com_winlator_xserver_Drawable_copyAreaOp(JNIEnv *env, jclass obj, jshort srcX, - jshort srcY, jshort dstX, jshort dstY, - jshort width, jshort height, jshort srcStride, - jshort dstStride, jobject srcData, - jobject dstData, int gcFunction) { + jshort srcY, jshort dstX, jshort dstY, + jshort width, jshort height, jshort srcStride, + jshort dstStride, jobject srcData, + jobject dstData, int gcFunction) { uint8_t *srcDataAddr = (*env)->GetDirectBufferAddress(env, srcData); uint8_t *dstDataAddr = (*env)->GetDirectBufferAddress(env, dstData); diff --git a/app/src/main/cpp/gpu_image.c b/app/src/main/cpp/gpu_image.c index 757b5d0..f0f3647 100644 --- a/app/src/main/cpp/gpu_image.c +++ b/app/src/main/cpp/gpu_image.c @@ -90,7 +90,6 @@ Java_com_winlator_renderer_GPUImage_lockHardwareBuffer(JNIEnv *env, jclass obj, JNIEXPORT void JNICALL Java_com_winlator_renderer_GPUImage_destroyImageKHR(JNIEnv *env, jclass obj, jlong imageKHRPtr) { - EGLImageKHR imageKHR = (EGLImageKHR)imageKHRPtr; if (imageKHR) { EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); diff --git a/app/src/main/cpp/proot/CMakeLists.txt b/app/src/main/cpp/proot/CMakeLists.txt new file mode 100644 index 0000000..45d97f7 --- /dev/null +++ b/app/src/main/cpp/proot/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.22.1) + +project(PRoot) + +set(CMAKE_C_FLAGS "-O2 -Wno-unused-function -Wimplicit-function-declaration") + +add_library(talloc STATIC + talloc/talloc.c) + +include_directories(src + talloc) + +add_executable(libproot.so + src/cli/cli.c + src/cli/proot.c + src/cli/note.c + src/execve/enter.c + src/execve/exit.c + src/execve/shebang.c + src/execve/elf.c + src/execve/auxv.c + src/execve/aoxp.c + src/path/binding.c + src/path/glue.c + src/path/canon.c + src/path/path.c + src/path/proc.c + src/path/temp.c + src/syscall/seccomp.c + src/syscall/syscall.c + src/syscall/chain.c + src/syscall/enter.c + src/syscall/exit.c + src/syscall/sysnum.c + src/syscall/socket.c + src/syscall/heap.c + src/syscall/rlimit.c + src/tracee/tracee.c + src/tracee/mem.c + src/tracee/reg.c + src/tracee/event.c + src/tracee/seccomp.c + src/ptrace/ptrace.c + src/ptrace/wait.c) + +target_link_libraries(libproot.so + talloc) + +add_library(proot-loader SHARED + src/loader/loader.c) \ No newline at end of file diff --git a/app/src/main/cpp/proot/src/COPYING b/app/src/main/cpp/proot/src/COPYING new file mode 100644 index 0000000..1f127a0 --- /dev/null +++ b/app/src/main/cpp/proot/src/COPYING @@ -0,0 +1,372 @@ +The copyright holder for PRoot and CARE is STMicroelectronics, these +tools are developed in the Compilation Expertise Center by: + +Cédric VINCENT + Original author, maintainer. + +Rémi DURAFFORT + Improved the -0 feature, many experiments, bug reports, fixes, and articles. + +Christophe GUILLON + Improved CARE, plugin experiments, bug reports, and fixes. + +Yves JANIN + Support for the i386 architecture, many experiments and bug reports. + +Antoine MOYNAULT + Many experiments and bug reports. + +Claire ROBINE + Many bug fixes. + +Clément BAZIN + Plugins interface, dependency tracker plugin, and bug reports. + +Christian BERTIN + Valuable support. + +Denis FERRANTI + Valuable support. + +Paul GHALEB + User manual review. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/app/src/main/cpp/proot/src/arch.h b/app/src/main/cpp/proot/src/arch.h new file mode 100644 index 0000000..64ad5ef --- /dev/null +++ b/app/src/main/cpp/proot/src/arch.h @@ -0,0 +1,104 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef ARCH_H +#define ARCH_H + +#ifndef NO_LIBC_HEADER +#include /* linux.git:c0a3a20b */ +#include /* AUDIT_ARCH_*, */ +#endif + +typedef unsigned long word_t; +typedef unsigned char byte_t; + +#define SYSCALL_AVOIDER ((word_t) -1) +#define SYSTRAP_NUM SYSARG_NUM +#define STACK_ALIGNMENT 16 + +#if defined(__ARM_EABI__) + #define ARCH_ARM_EABI 1 +#elif defined(__aarch64__) + #define ARCH_ARM64 1 +#elif defined(__arm__) + #error "Only EABI is currently supported for ARM" +#else + #error "Unsupported architecture" +#endif + +/* Architecture specific definitions. */ +#if defined(ARCH_ARM_EABI) + + #define SYSNUMS_HEADER1 "syscall/sysnums-arm.h" + #define SYSNUMS_ABI1 sysnums_arm + + #define SYSTRAP_SIZE 4 + + #define SECCOMP_ARCHS { { .value = AUDIT_ARCH_ARM, .nb_abis = 1, .abis = { ABI_DEFAULT } } } + + #define user_regs_struct user_regs + #define HOST_ELF_MACHINE {40, 0}; + #define RED_ZONE_SIZE 0 + #define OFFSETOF_STAT_UID_32 0 + #define OFFSETOF_STAT_GID_32 0 + #define EM_ARM 40 + + #define LOADER_ADDRESS 0x20000000 + + #define EXEC_PIC_ADDRESS 0x0f000000 + #define INTERP_PIC_ADDRESS 0x1f000000 + +#elif defined(ARCH_ARM64) + + #define SYSNUMS_HEADER1 "syscall/sysnums-arm64.h" + #define SYSNUMS_HEADER2 "syscall/sysnums-arm.h" + + #define SYSNUMS_ABI1 sysnums_arm64 + #define SYSNUMS_ABI2 sysnums_arm + + #define SYSTRAP_SIZE 4 + + #ifndef AUDIT_ARCH_AARCH64 + #define AUDIT_ARCH_AARCH64 (EM_AARCH64 | __AUDIT_ARCH_64BIT | __AUDIT_ARCH_LE) + #endif + + #define SECCOMP_ARCHS { \ + { .value = AUDIT_ARCH_AARCH64, .nb_abis = 1, .abis = { ABI_DEFAULT } }, \ + { .value = AUDIT_ARCH_ARM, .nb_abis = 1, .abis = { ABI_2 } }, \ + } + + #define HOST_ELF_MACHINE {183, 0}; + #define RED_ZONE_SIZE 0 + #define OFFSETOF_STAT_UID_32 24 + #define OFFSETOF_STAT_GID_32 28 + + #define LOADER_ADDRESS 0x2000000000 + #define EXEC_PIC_ADDRESS 0x3000000000 + #define INTERP_PIC_ADDRESS 0x3f00000000 + + #define HAS_LOADER_32BIT true + #define EXEC_PIC_ADDRESS_32 0x0f000000 + #define INTERP_PIC_ADDRESS_32 0x1f000000 + +#endif + +#endif /* ARCH_H */ diff --git a/app/src/main/cpp/proot/src/attribute.h b/app/src/main/cpp/proot/src/attribute.h new file mode 100644 index 0000000..f772954 --- /dev/null +++ b/app/src/main/cpp/proot/src/attribute.h @@ -0,0 +1,32 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef ATTRIBUTE_H +#define ATTRIBUTE_H + +#define UNUSED __attribute__((unused)) +#define FORMAT(a, b, c) __attribute__ ((format (a, b, c))) +#define DONT_INSTRUMENT __attribute__((no_instrument_function)) +#define PACKED __attribute__((packed)) +#define WEAK __attribute__((weak)) + +#endif /* ATTRIBUTE_H */ diff --git a/app/src/main/cpp/proot/src/cli/cli.c b/app/src/main/cpp/proot/src/cli/cli.c new file mode 100644 index 0000000..a8f8bd2 --- /dev/null +++ b/app/src/main/cpp/proot/src/cli/cli.c @@ -0,0 +1,444 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* printf(3), */ +#include /* bool, true, false, */ +#include /* ARG_MAX, PATH_MAX, */ +#include /* str*(3), basename(3), */ +#include /* talloc*, */ +#include /* exit(3), EXIT_*, strtol(3), {g,s}etenv(3), */ +#include /* assert(3), */ +#include /* getpid(2), */ +#include /* getpid(2), */ +#include /* errno(3), */ +#include /* INT_MAX, */ + +#include "cli/cli.h" +#include "cli/note.h" +#include "tracee/tracee.h" +#include "tracee/event.h" +#include "path/binding.h" +#include "path/canon.h" +#include "path/path.h" + +/** + * Print a (@detailed) usage of PRoot. + */ +void print_usage(Tracee *tracee, const Cli *cli, bool detailed) +{ + const char *current_class = "none"; + const Option *options; + size_t i, j; + +#define DETAIL(a) if (detailed) a + + DETAIL(printf("%s %s: %s.\n\n", cli->name, cli->version, cli->subtitle)); + printf("Usage:\n %s\n", cli->synopsis); + DETAIL(printf("\n")); + + options = cli->options; + for (i = 0; options[i].class != NULL; i++) { + for (j = 0; ; j++) { + const Argument *argument = &(options[i].arguments[j]); + + if (!argument->name || (!detailed && j != 0)) { + DETAIL(printf("\n")); + printf("\t%s\n", options[i].description); + if (detailed) { + if (options[i].detail[0] != '\0') + printf("\n%s\n\n", options[i].detail); + else + printf("\n"); + } + break; + } + + if (strcmp(options[i].class, current_class) != 0) { + current_class = options[i].class; + printf("\n%s:\n", current_class); + } + + if (j == 0) + printf(" %s", argument->name); + else + printf(", %s", argument->name); + + if (argument->separator != '\0') + printf("%c*%s*", argument->separator, argument->value); + else if (!detailed) + printf("\t"); + } + } + + if (detailed) + printf("%s\n", cli->colophon); +} + +/** + * Print the version of PRoot. + */ +void print_version(const Cli *cli) +{ + printf("%s %s\n\n", cli->logo, cli->version); +} + +static void print_execve_help(const Tracee *tracee, const char *argv0, int status) +{ + note(tracee, ERROR, SYSTEM, "execve(\"%s\")", argv0); + + note(tracee, INFO, USER, "possible causes:\n" +" * the program is a script but its interpreter (eg. /bin/sh) was not found;\n" +" * the program is an ELF but its interpreter (eg. ld-linux.so) was not found;\n" +" * the loader was not found or doesn't work."); +} + +static void print_error_separator(const Tracee *tracee, const Argument *argument) +{ + if (argument->separator == '\0') + note(tracee, ERROR, USER, "option '%s' expects no value.", argument->name); + else + note(tracee, ERROR, USER, "option '%s' and its value must be separated by '%c'.", + argument->name, argument->separator); +} + +static void print_argv(const Tracee *tracee, const char *prompt, char *const argv[]) +{ + char string[ARG_MAX] = ""; + size_t i; + + if (!argv) + return; + +#define APPEND(post) \ + do { \ + ssize_t length = sizeof(string) - (strlen(string) + strlen(post)); \ + if (length <= 0) \ + return; \ + strncat(string, post, length); \ + } while (0) + + APPEND(prompt); + APPEND(" ="); + for (i = 0; argv[i] != NULL; i++) { + APPEND(" "); + APPEND(argv[i]); + } + string[sizeof(string) - 1] = '\0'; + +#undef APPEND + + note(tracee, INFO, USER, "%s", string); +} + +static void print_config(Tracee *tracee, char *const argv[]) +{ + assert(tracee != NULL); + + if (tracee->verbose <= 0) + return; + + if (tracee->glue) + note(tracee, INFO, USER, "glue rootfs = %s", tracee->glue); + + note(tracee, INFO, USER, "exe = %s", tracee->exe); + print_argv(tracee, "argv", argv); + note(tracee, INFO, USER, "initial cwd = %s", tracee->fs->cwd); + note(tracee, INFO, USER, "verbose level = %d", tracee->verbose); +} + +/** + * Initialize @tracee's current working directory. This function + * returns -1 if an error occurred, otherwise 0. + */ +static int initialize_cwd(Tracee *tracee) +{ + char path2[PATH_MAX]; + char path[PATH_MAX]; + int status; + + /* Compute the base directory. */ + if (tracee->fs->cwd[0] != '/') { + strcpy(path, "/"); + } + else + strcpy(path, tracee->fs->cwd); + + /* The ending "." ensures canonicalize() will report an error + * if tracee->fs->cwd does not exist or if it is not a + * directory. */ + join_paths(path2, path, "."); + + /* Initiate state for canonicalization. */ + strcpy(path, "/"); + + status = canonicalize(tracee, path2, true, path, 0); + if (status < 0) { + note(tracee, WARNING, USER, "can't chdir(\"%s\") in the guest rootfs: %s", + path2, strerror(-status)); + note(tracee, INFO, USER, "default working directory is now \"/\""); + strcpy(path, "/"); + } + chop_finality(path); + + /* Replace with the canonicalized working directory. */ + TALLOC_FREE(tracee->fs->cwd); + tracee->fs->cwd = talloc_strdup(tracee->fs, path); + if (tracee->fs->cwd == NULL) + return -1; + talloc_set_name_const(tracee->fs->cwd, "$cwd"); + + /* Keep this special environment variable consistent. */ + setenv("PWD", path, 1); + + return 0; +} + +/** + * Initialize @tracee->exe from @exe, i.e. canonicalize it from a + * guest point-of-view. + */ +static int initialize_exe(Tracee *tracee, const char *exe) +{ + char path[PATH_MAX]; + int status; + + status = which(tracee, tracee->reconf.paths, path, exe ?: "/bin/sh"); + if (status < 0) + return -1; + + status = detranslate_path(tracee, path, NULL); + if (status < 0) + return -1; + + tracee->exe = talloc_strdup(tracee, path); + if (tracee->exe == NULL) + return -1; + talloc_set_name_const(tracee->exe, "$exe"); + + return 0; +} + +/** + * Configure @tracee according to the command-line arguments stored in + * @argv[]. This function returns the index in @argv[] of the command + * to launch, otherwise -1 if an error occured. + */ +static int parse_config(Tracee *tracee, size_t argc, char *const argv[]) +{ + option_handler_t handler = NULL; + const Option *options; + const Cli *cli = NULL; + size_t argc_offset; + size_t i, j, k; + int status; + + /* Unknown tool name? Default to PRoot. */ + if (cli == NULL) + cli = get_proot_cli(tracee->ctx); + + if (argc == 1) { + print_usage(tracee, cli, false); + return -1; + } + + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + /* The current argument is the value of a short option. */ + if (handler != NULL) { + status = handler(tracee, cli, arg); + if (status < 0) + return -1; + handler = NULL; + continue; + } + + if (arg[0] != '-') + break; /* End of PRoot options. */ + + options = cli->options; + for (j = 0; options[j].class != NULL; j++) { + const Option *option = &options[j]; + + /* A given option has several aliases. */ + for (k = 0; ; k++) { + const Argument *argument; + size_t length; + + argument = &option->arguments[k]; + + /* End of aliases for this option. */ + if (!argument->name) + break; + + length = strlen(argument->name); + if (strncmp(arg, argument->name, length) != 0) + continue; + + /* Avoid ambiguities. */ + if (strlen(arg) > length + && arg[length] != argument->separator) { + print_error_separator(tracee, argument); + return -1; + } + + /* No option value. */ + if (!argument->value) { + status = option->handler(tracee, cli, NULL); + if (status < 0) + return -1; + goto known_option; + } + + /* Value coalesced with to its option. */ + if (argument->separator == arg[length]) { + assert(strlen(arg) >= length); + status = option->handler(tracee, cli, &arg[length + 1]); + if (status < 0) + return -1; + goto known_option; + } + + /* Avoid ambiguities. */ + if (argument->separator != ' ') { + print_error_separator(tracee, argument); + return -1; + } + + /* Short option with a separated value. */ + handler = option->handler; + goto known_option; + } + } + + note(tracee, ERROR, USER, "unknown option '%s'.", arg); + return -1; + + known_option: + if (handler != NULL && i == argc - 1) { + note(tracee, ERROR, USER, "missing value for option '%s'.", arg); + return -1; + } + } + argc_offset = i; + + status = pre_initialize_bindings(tracee, cli); + if (status < 0) + return -1; + + /* The guest rootfs is now known: bindings specified by the + * user (tracee->bindings.user) can be canonicalized. */ + status = initialize_bindings(tracee); + if (status < 0) + return -1; + + /* Bindings are now installed (tracee->bindings.guest & + * tracee->bindings.host): the current working directory can + * be canonicalized. */ + status = initialize_cwd(tracee); + if (status < 0) + return -1; + + /* Bindings are now installed and the current working + * directory is canonicalized: resolve path to @tracee->exe + * and configure @tracee->cmdline. */ + status = initialize_exe(tracee, argv[argc_offset]); + if (status < 0) + return -1; + + print_config(tracee, &argv[argc_offset]); + + return argc_offset; +} + +bool exit_failure = true; + +int main(int argc, char *const argv[]) +{ + Tracee *tracee; + int status; + + /* Configure the memory allocator. */ + talloc_enable_leak_report(); + +#if defined(TALLOC_VERSION_MAJOR) && TALLOC_VERSION_MAJOR >= 2 + talloc_set_log_stderr(); +#endif + + /* Pre-create the first tracee (pid == 0). */ + tracee = get_tracee(NULL, 0, true); + if (tracee == NULL) + goto error; + tracee->pid = getpid(); + + /* Set verboseness from env variable, may be overriden by option */ + { + const char *verbose_env = getenv("PROOT_VERBOSE"); + if (verbose_env != NULL) { + tracee->verbose = strtol(verbose_env, NULL, 10); + global_verbose_level = tracee->verbose; + } + } + + /* Pre-configure the first tracee. */ + status = parse_config(tracee, argc, argv); + if (status < 0) + goto error; + + /* Start the first tracee. */ + status = launch_process(tracee, &argv[status]); + if (status < 0) { + print_execve_help(tracee, tracee->exe, status); + goto error; + } + + /* Start tracing the first tracee and all its children. */ + exit(event_loop()); + +error: + TALLOC_FREE(tracee); + + if (exit_failure) { + exit(EXIT_FAILURE); + } + else + exit(EXIT_SUCCESS); +} + +/** + * Convert @value into an integer, then put the result into + * *@variable. This function prints a warning and returns -1 if a + * conversion error occured, otherwise it returns 0. + */ +int parse_integer_option(const Tracee *tracee, int *variable, const char *value, const char *option) +{ + char *end_ptr = NULL; + + errno = 0; + *variable = strtol(value, &end_ptr, 10); + if (errno != 0 || end_ptr == value) { + note(tracee, ERROR, USER, "option `%s` expects an integer value.", option); + return -1; + } + + return 0; +} diff --git a/app/src/main/cpp/proot/src/cli/cli.h b/app/src/main/cpp/proot/src/cli/cli.h new file mode 100644 index 0000000..6827fb8 --- /dev/null +++ b/app/src/main/cpp/proot/src/cli/cli.h @@ -0,0 +1,56 @@ +/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ + +#ifndef CLI_H +#define CLI_H + +#include +#include "tracee/tracee.h" +#include "attribute.h" + +typedef struct { + const char *name; + char separator; + const char *value; +} Argument; + +struct Cli; +typedef int (*option_handler_t)(Tracee *tracee, const struct Cli *cli, const char *value); + +typedef struct { + const char *class; + option_handler_t handler; + const char *description; + const char *detail; + Argument arguments[5]; +} Option; + +#define END_OF_OPTIONS { .class = NULL, \ + .arguments = {{ .name = NULL, .separator = '\0', .value = NULL }}, \ + .handler = NULL, \ + .description = NULL, \ + .detail = NULL \ + } + +typedef struct Cli { + const char *name; + const char *version; + const char *subtitle; + const char *synopsis; + const char *colophon; + const char *logo; + + void *private; + + const Option options[]; +} Cli; + +extern int pre_initialize_bindings(Tracee *, const Cli *); +extern const Cli *get_proot_cli(TALLOC_CTX *context); + +extern void print_usage(Tracee *tracee, const Cli *cli, bool detailed); +extern void print_version(const Cli *cli); +extern int parse_integer_option(const Tracee *tracee, int *variable, const char *value, const char *option); + +extern bool exit_failure; + +#endif /* CLI_H */ diff --git a/app/src/main/cpp/proot/src/cli/note.c b/app/src/main/cpp/proot/src/cli/note.c new file mode 100644 index 0000000..aad7f1d --- /dev/null +++ b/app/src/main/cpp/proot/src/cli/note.c @@ -0,0 +1,95 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* errno, */ +#include /* strerror(3), */ +#include /* va_*, */ +#include /* vfprintf(3), */ +#include /* INT_MAX, */ + +#include "cli/note.h" +#include "tracee/tracee.h" + +int global_verbose_level; +const char *global_tool_name; + +/** + * Print @message to the standard error stream according to its + * @severity and @origin. + */ +void note(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) +{ + const char *tool_name; + va_list extra_params; + int verbose_level; + + tool_name = global_tool_name ?: ""; + if (tracee == NULL) { + verbose_level = global_verbose_level; + } + else + verbose_level = tracee->verbose; + + if (verbose_level < 0 && severity != ERROR) + return; + + switch (severity) { + case WARNING: + fprintf(stderr, "%s warning: ", tool_name); + break; + + case ERROR: + fprintf(stderr, "%s error: ", tool_name); + break; + + case INFO: + default: + fprintf(stderr, "%s info: ", tool_name); + break; + } + + if (origin == TALLOC) + fprintf(stderr, "talloc: "); + + va_start(extra_params, message); + vfprintf(stderr, message, extra_params); + va_end(extra_params); + + switch (origin) { + case SYSTEM: + fprintf(stderr, ": "); + perror(NULL); + break; + + case TALLOC: + break; + + case INTERNAL: + case USER: + default: + fprintf(stderr, "\n"); + break; + } + + return; +} + diff --git a/app/src/main/cpp/proot/src/cli/note.h b/app/src/main/cpp/proot/src/cli/note.h new file mode 100644 index 0000000..8c73f84 --- /dev/null +++ b/app/src/main/cpp/proot/src/cli/note.h @@ -0,0 +1,54 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef NOTE_H +#define NOTE_H + +#include "tracee/tracee.h" +#include "attribute.h" + +/* Specify where a notice is coming from. */ +typedef enum { + SYSTEM, + INTERNAL, + USER, + TALLOC, +} Origin; + +/* Specify the severity of a notice. */ +typedef enum { + ERROR, + WARNING, + INFO, +} Severity; + +#define VERBOSE(tracee, level, message, args...) do { \ + if (tracee == NULL || tracee->verbose >= (level)) \ + note(tracee, INFO, INTERNAL, (message), ## args); \ + } while (0) + +extern void note(const Tracee *tracee, Severity severity, Origin origin, const char *message, ...) FORMAT(printf, 4, 5); + +extern int global_verbose_level; +extern const char *global_tool_name; + +#endif /* NOTE_H */ diff --git a/app/src/main/cpp/proot/src/cli/proot.c b/app/src/main/cpp/proot/src/cli/proot.c new file mode 100644 index 0000000..2a3f812 --- /dev/null +++ b/app/src/main/cpp/proot/src/cli/proot.c @@ -0,0 +1,152 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* str*(3), */ +#include /* assert(3), */ +#include /* printf(3), fflush(3), */ +#include /* write(2), */ + +#include "cli/cli.h" +#include "cli/note.h" +#include "path/binding.h" +#include "attribute.h" + +/* These should be included last. */ +#include "cli/proot.h" + +static int handle_option_r(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + Binding *binding; + + /* ``chroot $PATH`` is semantically equivalent to ``mount + * --bind $PATH /``. */ + binding = new_binding(tracee, value, "/", true); + if (binding == NULL) + return -1; + + return 0; +} + +static int handle_option_b(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + char *host; + char *guest; + + host = talloc_strdup(tracee->ctx, value); + if (host == NULL) { + note(tracee, ERROR, INTERNAL, "can't allocate memory"); + return -1; + } + + guest = strchr(host, ':'); + if (guest != NULL) { + *guest = '\0'; + guest++; + } + + new_binding(tracee, host, guest, true); + return 0; +} + +static int handle_option_w(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + tracee->fs->cwd = talloc_strdup(tracee->fs, value); + if (tracee->fs->cwd == NULL) + return -1; + talloc_set_name_const(tracee->fs->cwd, "$cwd"); + return 0; +} + +static int handle_option_kill_on_exit(Tracee *tracee, const Cli *cli UNUSED, const char *value UNUSED) +{ + tracee->killall_on_exit = true; + return 0; +} + +static int handle_option_v(Tracee *tracee, const Cli *cli UNUSED, const char *value) +{ + int status; + + status = parse_integer_option(tracee, &tracee->verbose, value, "-v"); + if (status < 0) + return status; + + global_verbose_level = tracee->verbose; + return 0; +} + +extern unsigned char WEAK _binary_licenses_start; +extern unsigned char WEAK _binary_licenses_end; + +static int handle_option_V(Tracee *tracee UNUSED, const Cli *cli, const char *value UNUSED) +{ + size_t size; + + print_version(cli); + printf("\n%s\n", cli->colophon); + fflush(stdout); + + size = &_binary_licenses_end - &_binary_licenses_start; + if (size > 0) + write(1, &_binary_licenses_start, size); + + exit_failure = false; + return -1; +} + +static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value UNUSED) +{ + print_usage(tracee, cli, true); + exit_failure = false; + return -1; +} + +/** + * Initialize @tracee's fields that are mandatory for PRoot but that + * are not required on the command line, i.e. "-w" and "-r". + */ +int pre_initialize_bindings(Tracee *tracee, const Cli *cli) +{ + int status; + + /* Default to "." if no CWD were specified. */ + if (tracee->fs->cwd == NULL) { + status = handle_option_w(tracee, cli, "."); + if (status < 0) + return -1; + } + + /* The default guest rootfs is "/" if none was specified. */ + if (get_root(tracee) == NULL) { + status = handle_option_r(tracee, cli, "/"); + if (status < 0) + return -1; + } + + return 0; +} + +const Cli *get_proot_cli(TALLOC_CTX *context UNUSED) +{ + global_tool_name = proot_cli.name; + return &proot_cli; +} diff --git a/app/src/main/cpp/proot/src/cli/proot.h b/app/src/main/cpp/proot/src/cli/proot.h new file mode 100644 index 0000000..4ba5628 --- /dev/null +++ b/app/src/main/cpp/proot/src/cli/proot.h @@ -0,0 +1,127 @@ +/* This file is automatically generated from the documentation. EDIT AT YOUR OWN RISK. */ + +#ifndef PROOT_CLI_H +#define PROOT_CLI_H + +#include "cli/cli.h" + +#ifndef VERSION +#define VERSION "5.1.0" +#endif + +static int handle_option_r(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_b(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_w(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_v(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_V(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_h(Tracee *tracee, const Cli *cli, const char *value); +static int handle_option_kill_on_exit(Tracee *tracee, const Cli *cli, const char *value); + +static Cli proot_cli = { + .version = VERSION, + .name = "proot", + .subtitle = "chroot, mount --bind, and binfmt_misc without privilege/setup", + .synopsis = "proot [option] ... [command]", + .colophon = "Visit http://proot.me for help, bug reports, suggestions, patchs, ...\n\ +Copyright (C) 2015 STMicroelectronics, licensed under GPL v2 or later.", + .logo = "\ + _____ _____ ___\n\ +| __ \\ __ \\_____ _____| |_\n\ +| __/ / _ \\/ _ \\ _|\n\ +|__| |__|__\\_____/\\_____/\\____|", + + .options = { + { .class = "Regular options", + .arguments = { + { .name = "-r", .separator = ' ', .value = "path" }, + { .name = "--rootfs", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_r, + .description = "Use *path* as the new guest root file-system, default is /.", + .detail = "\tThe specified path typically contains a Linux distribution where\n\ +\tall new programs will be confined. The default rootfs is /\n\ +\twhen none is specified, this makes sense when the bind mechanism\n\ +\tis used to relocate host files and directories, see the -b\n\ +\toption and the Examples section for details.\n\ +\t\n\ +\tIt is recommended to use the -R or -S options instead.", + }, + { .class = "Regular options", + .arguments = { + { .name = "-b", .separator = ' ', .value = "path" }, + { .name = "--bind", .separator = '=', .value = "path" }, + { .name = "-m", .separator = ' ', .value = "path" }, + { .name = "--mount", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_b, + .description = "Make the content of *path* accessible in the guest rootfs.", + .detail = "\tThis option makes any file or directory of the host rootfs\n\ +\taccessible in the confined environment just as if it were part of\n\ +\tthe guest rootfs. By default the host path is bound to the same\n\ +\tpath in the guest rootfs but users can specify any other location\n\ +\twith the syntax: -b *host_path*:*guest_location*. If the\n\ +\tguest location is a symbolic link, it is dereferenced to ensure\n\ +\tthe new content is accessible through all the symbolic links that\n\ +\tpoint to the overlaid content. In most cases this default\n\ +\tbehavior shouldn't be a problem, although it is possible to\n\ +\texplicitly not dereference the guest location by appending it the\n\ +\t! character: -b *host_path*:*guest_location!*.", + }, + { .class = "Regular options", + .arguments = { + { .name = "-w", .separator = ' ', .value = "path" }, + { .name = "--pwd", .separator = '=', .value = "path" }, + { .name = "--cwd", .separator = '=', .value = "path" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_w, + .description = "Set the initial working directory to *path*.", + .detail = "\tSome programs expect to be launched from a given directory but do\n\ +\tnot perform any chdir by themselves. This option avoids the\n\ +\tneed for running a shell and then entering the directory manually.", + }, + { .class = "Regular options", + .arguments = { + { .name = "--kill-on-exit", .separator = '\0', .value = NULL }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_kill_on_exit, + .description = "Kill all processes on command exit.", + .detail = "\tWhen the executed command leaves orphan or detached processes\n\ +\taround, proot waits until all processes possibly terminate. This option forces\n\ +\tthe immediate termination of all tracee processes when the main command exits.", + }, + { .class = "Regular options", + .arguments = { + { .name = "-v", .separator = ' ', .value = "value" }, + { .name = "--verbose", .separator = '=', .value = "value" }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_v, + .description = "Set the level of debug information to *value*.", + .detail = "\tThe higher the integer value is, the more detailed debug\n\ +\tinformation is printed to the standard error stream. A negative\n\ +\tvalue makes PRoot quiet except on fatal errors.", + }, + { .class = "Regular options", + .arguments = { + { .name = "-V", .separator = '\0', .value = NULL }, + { .name = "--version", .separator = '\0', .value = NULL }, + { .name = "--about", .separator = '\0', .value = NULL }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_V, + .description = "Print version, copyright, license and contact, then exit.", + .detail = "", + }, + { .class = "Regular options", + .arguments = { + { .name = "-h", .separator = '\0', .value = NULL }, + { .name = "--help", .separator = '\0', .value = NULL }, + { .name = "--usage", .separator = '\0', .value = NULL }, + { .name = NULL, .separator = '\0', .value = NULL } }, + .handler = handle_option_h, + .description = "Print the version and the command-line usage, then exit.", + .detail = "", + }, + END_OF_OPTIONS, + }, +}; + +#endif /* PROOT_CLI_H */ diff --git a/app/src/main/cpp/proot/src/compat.h b/app/src/main/cpp/proot/src/compat.h new file mode 100644 index 0000000..f69bc3f --- /dev/null +++ b/app/src/main/cpp/proot/src/compat.h @@ -0,0 +1,265 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef COMPAT_H +#define COMPAT_H + +/* Local definitions for compatibility with old and/or broken distros... */ +# ifndef AT_NULL +# define AT_NULL 0 +# endif +# ifndef AT_PHDR +# define AT_PHDR 3 +# endif +# ifndef AT_PHENT +# define AT_PHENT 4 +# endif +# ifndef AT_PHNUM +# define AT_PHNUM 5 +# endif +# ifndef AT_BASE +# define AT_BASE 7 +# endif +# ifndef AT_ENTRY +# define AT_ENTRY 9 +# endif +# ifndef AT_RANDOM +# define AT_RANDOM 25 +# endif +# ifndef AT_EXECFN +# define AT_EXECFN 31 +# endif +# ifndef AT_SYSINFO +# define AT_SYSINFO 32 +# endif +# ifndef AT_SYSINFO_EHDR +# define AT_SYSINFO_EHDR 33 +# endif +# ifndef AT_FDCWD +# define AT_FDCWD -100 +# endif +# ifndef AT_SYMLINK_FOLLOW +# define AT_SYMLINK_FOLLOW 0x400 +# endif +# ifndef AT_REMOVEDIR +# define AT_REMOVEDIR 0x200 +# endif +# ifndef AT_SYMLINK_NOFOLLOW +# define AT_SYMLINK_NOFOLLOW 0x100 +# endif +# ifndef IN_DONT_FOLLOW +# define IN_DONT_FOLLOW 0x02000000 +# endif +# ifndef WIFCONTINUED +# define WIFCONTINUED(status) ((status) == 0xffff) +# endif +# ifndef PTRACE_GETREGS +# define PTRACE_GETREGS 12 +# endif +# ifndef PTRACE_SETREGS +# define PTRACE_SETREGS 13 +# endif +# ifndef PTRACE_GETFPREGS +# define PTRACE_GETFPREGS 14 +# endif +# ifndef PTRACE_SETFPREGS +# define PTRACE_SETFPREGS 15 +# endif +# ifndef PTRACE_GETFPXREGS +# define PTRACE_GETFPXREGS 18 +# endif +# ifndef PTRACE_SETFPXREGS +# define PTRACE_SETFPXREGS 19 +# endif +# ifndef PTRACE_SETOPTIONS +# define PTRACE_SETOPTIONS 0x4200 +# endif +# ifndef PTRACE_GETEVENTMSG +# define PTRACE_GETEVENTMSG 0x4201 +# endif +# ifndef PTRACE_GETREGSET +# define PTRACE_GETREGSET 0x4204 +# endif +# ifndef PTRACE_SETREGSET +# define PTRACE_SETREGSET 0x4205 +# endif +# ifndef PTRACE_SEIZE +# define PTRACE_SEIZE 0x4206 +# endif +# ifndef PTRACE_INTERRUPT +# define PTRACE_INTERRUPT 0x4207 +# endif +# ifndef PTRACE_LISTEN +# define PTRACE_LISTEN 0x4208 +# endif +# ifndef PTRACE_O_TRACESYSGOOD +# define PTRACE_O_TRACESYSGOOD 0x00000001 +# endif +# ifndef PTRACE_O_TRACEFORK +# define PTRACE_O_TRACEFORK 0x00000002 +# endif +# ifndef PTRACE_O_TRACEVFORK +# define PTRACE_O_TRACEVFORK 0x00000004 +# endif +# ifndef PTRACE_O_TRACECLONE +# define PTRACE_O_TRACECLONE 0x00000008 +# endif +# ifndef PTRACE_O_TRACEEXEC +# define PTRACE_O_TRACEEXEC 0x00000010 +# endif +# ifndef PTRACE_O_TRACEVFORKDONE +# define PTRACE_O_TRACEVFORKDONE 0x00000020 +# endif +# ifndef PTRACE_O_TRACEEXIT +# define PTRACE_O_TRACEEXIT 0x00000040 +# endif +# ifndef PTRACE_O_TRACESECCOMP +# define PTRACE_O_TRACESECCOMP 0x00000080 +# endif +# ifndef PTRACE_EVENT_FORK +# define PTRACE_EVENT_FORK 1 +# endif +# ifndef PTRACE_EVENT_VFORK +# define PTRACE_EVENT_VFORK 2 +# endif +# ifndef PTRACE_EVENT_CLONE +# define PTRACE_EVENT_CLONE 3 +# endif +# ifndef PTRACE_EVENT_EXEC +# define PTRACE_EVENT_EXEC 4 +# endif +# ifndef PTRACE_EVENT_VFORK_DONE +# define PTRACE_EVENT_VFORK_DONE 5 +# endif +# ifndef PTRACE_EVENT_EXIT +# define PTRACE_EVENT_EXIT 6 +# endif +# ifndef PTRACE_EVENT_SECCOMP +# define PTRACE_EVENT_SECCOMP 7 +# endif +# ifndef PTRACE_EVENT_SECCOMP2 +# if PTRACE_EVENT_SECCOMP == 7 +# define PTRACE_EVENT_SECCOMP2 8 +# elif PTRACE_EVENT_SECCOMP == 8 +# define PTRACE_EVENT_SECCOMP2 7 +# else +# error "unknown PTRACE_EVENT_SECCOMP value" +# endif +# endif +# ifndef PTRACE_SET_SYSCALL +# define PTRACE_SET_SYSCALL 23 +# endif +# ifndef PTRACE_GET_THREAD_AREA +# define PTRACE_GET_THREAD_AREA 25 +# endif +# ifndef PTRACE_SET_THREAD_AREA +# define PTRACE_SET_THREAD_AREA 26 +# endif +# ifndef PTRACE_GETVFPREGS +# define PTRACE_GETVFPREGS 27 +# endif +# ifndef PTRACE_ARCH_PRCTL +# define PTRACE_ARCH_PRCTL 30 +# endif +# ifndef ARCH_SET_GS +# define ARCH_SET_GS 0x1001 +# endif +# ifndef ARCH_SET_FS +# define ARCH_SET_FS 0x1002 +# endif +# ifndef ARCH_GET_GS +# define ARCH_GET_FS 0x1003 +# endif +# ifndef ARCH_GET_FS +# define ARCH_GET_GS 0x1004 +# endif +# ifndef PTRACE_SINGLEBLOCK +# define PTRACE_SINGLEBLOCK 33 +# endif +# ifndef ADDR_NO_RANDOMIZE +# define ADDR_NO_RANDOMIZE 0x0040000 +# endif +# ifndef SYS_ACCEPT4 +# define SYS_ACCEPT4 18 +# endif +# ifndef TALLOC_FREE +# define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx = NULL; } while(0) +# endif +# ifndef PR_SET_NAME +# define PR_SET_NAME 15 +# endif +# ifndef PR_SET_NO_NEW_PRIVS +# define PR_SET_NO_NEW_PRIVS 38 +# endif +# ifndef PR_SET_SECCOMP +# define PR_SET_SECCOMP 22 +# endif +# ifndef SECCOMP_MODE_FILTER +# define SECCOMP_MODE_FILTER 2 +# endif +# ifndef talloc_get_type_abort +# define talloc_get_type_abort talloc_get_type +# endif +# ifndef FUTEX_PRIVATE_FLAG +# define FUTEX_PRIVATE_FLAG 128 +# endif +# ifndef EFD_SEMAPHORE +# define EFD_SEMAPHORE 1 +# endif +# ifndef F_DUPFD_CLOEXEC +# define F_DUPFD_CLOEXEC 1030 +# endif +# ifndef O_RDONLY +# define O_RDONLY 00000000 +# endif +# ifndef O_CLOEXEC +# define O_CLOEXEC 02000000 +# endif +# ifndef MAP_PRIVATE +# define MAP_PRIVATE 0x02 +# endif +# ifndef MAP_FIXED +# define MAP_FIXED 0x10 +# endif +# ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS 0x20 +# endif +# ifndef PROT_READ +# define PROT_READ 0x1 +# endif +# ifndef PROT_WRITE +# define PROT_WRITE 0x2 +# endif +# ifndef PROT_EXEC +# define PROT_EXEC 0x4 +# endif +# ifndef PROT_GROWSDOWN +# define PROT_GROWSDOWN 0x01000000 +# endif +# ifndef NT_ARM_SYSTEM_CALL +# define NT_ARM_SYSTEM_CALL 0x404 +# endif +# ifndef SYS_SECCOMP +# define SYS_SECCOMP 1 +# endif + +#endif /* COMPAT_H */ diff --git a/app/src/main/cpp/proot/src/execve/aoxp.c b/app/src/main/cpp/proot/src/execve/aoxp.c new file mode 100644 index 0000000..b818651 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/aoxp.c @@ -0,0 +1,336 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* ARG_MAX, */ +#include /* assert(3), */ +#include /* strlen(3), memcmp(3), memcpy(3), */ +#include /* bzero(3), */ +#include /* bool, true, false, */ +#include /* E*, */ +#include /* va_*, */ +#include /* uint32_t, */ +#include /* talloc_*, */ + +#include "arch.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "tracee/abi.h" + +struct mixed_pointer { + /* Pointer -- in tracee's address space -- to the current + * object, if local == NULL. */ + word_t remote; + + /* Pointer -- in tracer's address space -- to the current + * object, if local != NULL. */ + void *local; +}; + +#include "execve/aoxp.h" + +/** + * Read string pointed to by @array[@index] from tracee's memory, then + * make @local_pointer points to the locally *cached* version. This + * function returns -errno when an error occured, otherwise 0. + */ +int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **local_pointer) +{ + char tmp[ARG_MAX]; + int status; + + assert(index < array->length); + + /* Already cached locally? */ + if (array->_xpointers[index].local != NULL) + goto end; + + /* Remote NULL is mapped to local NULL. */ + if (array->_xpointers[index].remote == 0) { + array->_xpointers[index].local = NULL; + goto end; + } + + /* Copy locally the remote string into a temporary buffer. */ + status = read_string(TRACEE(array), tmp, array->_xpointers[index].remote, ARG_MAX); + if (status < 0) + return status; + if (status >= ARG_MAX) + return -ENOMEM; + + /* Save the local string in a "persistent" buffer. */ + array->_xpointers[index].local = talloc_strdup(array, tmp); + if (array->_xpointers[index].local == NULL) + return -ENOMEM; + +end: + *local_pointer = array->_xpointers[index].local; + return 0; +} + +/** + * This function returns the number of bytes of the string pointed to + * by @array[@index], otherwise -errno if an error occured. + */ +int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index) +{ + char *string; + int status; + + assert(index < array->length); + + status = read_xpointee_as_string(array, index, &string); + if (status < 0) + return status; + + if (string == NULL) + return 0; + + return strlen(string) + 1; +} + +/** + * Make @array[@index] points to a copy of the string pointed to by + * @string. This function returns -errno when an error occured, + * otherwise 0. + */ +int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string) +{ + assert(index < array->length); + + array->_xpointers[index].local = talloc_strdup(array, string); + if (array->_xpointers[index].local == NULL) + return -ENOMEM; + + return 0; +} + +/** + * Make @array[@index ... @index + @nb_xpointees] points to a copy of + * the variadic arguments. This function returns -errno when an error + * occured, otherwise 0. + */ +int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...) +{ + va_list va_xpointees; + int status; + size_t i; + + va_start(va_xpointees, nb_xpointees); + + for (i = 0; i < nb_xpointees; i++) { + void *object = va_arg(va_xpointees, void *); + + status = write_xpointee(array, index + i, object); + if (status < 0) + goto end; + } + status = 0; + +end: + va_end(va_xpointees); + return status; +} + + +/** + * Resize the @array at the given @index by the @delta_nb_entries. + * This function returns -errno when an error occured, otherwise 0. + */ +int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t delta_nb_entries) +{ + size_t nb_moved_entries; + size_t new_length; + void *tmp; + + assert(index < array->length); + + if (delta_nb_entries == 0) + return 0; + + new_length = array->length + delta_nb_entries; + nb_moved_entries = array->length - index; + + if (delta_nb_entries > 0) { + tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length); + if (tmp == NULL) + return -ENOMEM; + array->_xpointers = tmp; + + memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index, + nb_moved_entries * sizeof(XPointer)); + + bzero(array->_xpointers + index, delta_nb_entries * sizeof(XPointer)); + } + else { + assert(delta_nb_entries <= 0); + assert(index >= (size_t) -delta_nb_entries); + + memmove(array->_xpointers + index + delta_nb_entries, array->_xpointers + index, + nb_moved_entries * sizeof(XPointer)); + + tmp = talloc_realloc(array, array->_xpointers, XPointer, new_length); + if (tmp == NULL) + return -ENOMEM; + array->_xpointers = tmp; + } + + array->length = new_length; + return 0; +} + +/** + * Copy into *@array_ the pointer array pointed to by @reg from + * @tracee's memory space. Only the first @nb_entries are copied, + * unless it is 0 then all the entries up to the NULL pointer are + * copied. This function returns -errno when an error occured, + * otherwise 0. + */ +int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array_, Reg reg, size_t nb_entries) +{ + word_t pointer = 1; /* ie. != 0 */ + word_t address; + ArrayOfXPointers *array; + size_t i; + + assert(array_ != NULL); + + *array_ = talloc_zero(tracee->ctx, ArrayOfXPointers); + if (*array_ == NULL) + return -ENOMEM; + array = *array_; + + address = peek_reg(tracee, CURRENT, reg); + + for (i = 0; nb_entries != 0 ? i < nb_entries : pointer != 0; i++) { + void *tmp = talloc_realloc(array, array->_xpointers, XPointer, i + 1); + if (tmp == NULL) + return -ENOMEM; + array->_xpointers = tmp; + + pointer = peek_word(tracee, address + i * sizeof_word(tracee)); + if (errno != 0) + return -errno; + + array->_xpointers[i].remote = pointer; + array->_xpointers[i].local = NULL; + } + array->length = i; + + /* By default, assume it is an array of string pointers. */ + array->sizeof_xpointee = sizeof_xpointee_as_string; + array->write_xpointee = (write_xpointee_t) write_xpointee_as_string; + + return 0; +} + +/** + * Copy @array into tracee's memory space, then put in @reg the + * address where it was copied. This function returns -errno if an + * error occured, otherwise 0. + */ +int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg) +{ + Tracee *tracee; + struct iovec *local; + size_t local_count; + size_t total_size; + word_t *pod_array; + word_t tracee_ptr; + int status; + size_t i; + + /* Nothing to do, for sure. */ + if (array == NULL) + return 0; + + tracee = TRACEE(array); + + /* The pointer table is a POD array in the tracee's memory. */ + pod_array = talloc_zero_size(tracee->ctx, array->length * sizeof_word(tracee)); + if (pod_array == NULL) + return -ENOMEM; + + /* There's one vector per modified pointee + one vector for the + * pod array. */ + local = talloc_zero_array(tracee->ctx, struct iovec, array->length + 1); + if (local == NULL) + return -ENOMEM; + + /* The pod array is expected to be at the beginning of the + * allocated memory by the caller. */ + total_size = array->length * sizeof_word(tracee); + local[0].iov_base = pod_array; + local[0].iov_len = total_size; + local_count = 1; + + /* Create one vector for each modified pointee. */ + for (i = 0; i < array->length; i++) { + ssize_t size; + + if (array->_xpointers[i].local == NULL) + continue; + + /* At this moment, we only know the offsets in the + * tracee's memory block. */ + array->_xpointers[i].remote = total_size; + + size = sizeof_xpointee(array, i); + if (size < 0) + return size; + total_size += size; + + local[local_count].iov_base = array->_xpointers[i].local; + local[local_count].iov_len = size; + local_count++; + } + + /* Nothing has changed, don't update anything. */ + if (local_count == 1) + return 0; + assert(local_count < array->length + 1); + + /* Modified pointees and the pod array are stored in a tracee's + * memory block. */ + tracee_ptr = alloc_mem(tracee, total_size); + if (tracee_ptr == 0) + return -E2BIG; + + /* Now, we know the absolute addresses in the tracee's + * memory. */ + for (i = 0; i < array->length; i++) { + if (array->_xpointers[i].local != NULL) + array->_xpointers[i].remote += tracee_ptr; + + if (is_32on64_mode(tracee)) + ((uint32_t *) pod_array)[i] = array->_xpointers[i].remote; + else + pod_array[i] = array->_xpointers[i].remote; + } + + /* Write all the modified pointees and the pod array at once. */ + status = writev_data(tracee, tracee_ptr, local, local_count); + if (status < 0) + return status; + + poke_reg(tracee, reg, tracee_ptr); + return 0; +} diff --git a/app/src/main/cpp/proot/src/execve/aoxp.h b/app/src/main/cpp/proot/src/execve/aoxp.h new file mode 100644 index 0000000..5760dbb --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/aoxp.h @@ -0,0 +1,63 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef AOXP_H +#define AOXP_H + +#include + +#include "tracee/reg.h" +#include "arch.h" + +typedef struct array_of_xpointers ArrayOfXPointers; +typedef int (*write_xpointee_t)(ArrayOfXPointers *array, size_t index, const void *object); +typedef int (*sizeof_xpointee_t)(ArrayOfXPointers *array, size_t index); + +typedef struct mixed_pointer XPointer; +struct array_of_xpointers { + XPointer *_xpointers; + size_t length; + + write_xpointee_t write_xpointee; + sizeof_xpointee_t sizeof_xpointee; +}; + +static inline int write_xpointee(ArrayOfXPointers *array, size_t index, const void *object) +{ + return array->write_xpointee(array, index, object); +} + +static inline int sizeof_xpointee(ArrayOfXPointers *array, size_t index) +{ + return array->sizeof_xpointee(array, index); +} + +extern int resize_array_of_xpointers(ArrayOfXPointers *array, size_t index, ssize_t nb_delta_entries); +extern int fetch_array_of_xpointers(Tracee *tracee, ArrayOfXPointers **array, Reg reg, size_t nb_entries); +extern int push_array_of_xpointers(ArrayOfXPointers *array, Reg reg); + +extern int read_xpointee_as_string(ArrayOfXPointers *array, size_t index, char **string); +extern int write_xpointee_as_string(ArrayOfXPointers *array, size_t index, const char *string); +extern int write_xpointees(ArrayOfXPointers *array, size_t index, size_t nb_xpointees, ...); +extern int sizeof_xpointee_as_string(ArrayOfXPointers *array, size_t index); + +#endif /* AOXP_H */ diff --git a/app/src/main/cpp/proot/src/execve/auxv.c b/app/src/main/cpp/proot/src/execve/auxv.c new file mode 100644 index 0000000..7ab0779 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/auxv.c @@ -0,0 +1,150 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* AT_*, */ +#include /* assert(3), */ +#include /* E*, */ +#include /* write(3), close(3), */ +#include /* open(2), */ +#include /* open(2), */ +#include /* open(2), */ + +#include "execve/auxv.h" +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "arch.h" + + +/** + * Add the given vector [@type, @value] to @vectors. This function + * returns -errno if an error occurred, otherwise 0. + */ +int add_elf_aux_vector(ElfAuxVector **vectors, word_t type, word_t value) +{ + ElfAuxVector *tmp; + size_t nb_vectors; + + assert(*vectors != NULL); + + nb_vectors = talloc_array_length(*vectors); + + /* Sanity checks. */ + assert(nb_vectors > 0); + assert((*vectors)[nb_vectors - 1].type == AT_NULL); + + tmp = talloc_realloc(talloc_parent(*vectors), *vectors, ElfAuxVector, nb_vectors + 1); + if (tmp == NULL) + return -ENOMEM; + *vectors = tmp; + + /* Replace the sentinel with the new vector. */ + (*vectors)[nb_vectors - 1].type = type; + (*vectors)[nb_vectors - 1].value = value; + + /* Restore the sentinel. */ + (*vectors)[nb_vectors].type = AT_NULL; + (*vectors)[nb_vectors].value = 0; + + return 0; +} + +/** + * Get the address of the the ELF auxiliary vectors table for the + * given @tracee. This function returns 0 if an error occurred. + */ +word_t get_elf_aux_vectors_address(const Tracee *tracee) +{ + word_t address; + word_t data; + + /* Sanity check: this works only in execve sysexit. */ + assert(IS_IN_SYSEXIT2(tracee, PR_execve)); + + /* Right after execve, the stack layout is: + * + * argc, argv[0], ..., 0, envp[0], ..., 0, auxv[0].type, auxv[0].value, ..., 0, 0 + */ + address = peek_reg(tracee, CURRENT, STACK_POINTER); + + /* Read: argc */ + data = peek_word(tracee, address); + if (errno != 0) + return 0; + + /* Skip: argc, argv, 0 */ + address += (1 + data + 1) * sizeof_word(tracee); + + /* Skip: envp, 0 */ + do { + data = peek_word(tracee, address); + if (errno != 0) + return 0; + address += sizeof_word(tracee); + } while (data != 0); + + return address; +} + +/** + * Fetch ELF auxiliary vectors stored at the given @address in + * @tracee's memory. This function returns NULL if an error occurred, + * otherwise it returns a pointer to the new vectors, in an ABI + * independent form (the Talloc parent of this pointer is + * @tracee->ctx). + */ +ElfAuxVector *fetch_elf_aux_vectors(const Tracee *tracee, word_t address) +{ + ElfAuxVector *vectors = NULL; + ElfAuxVector vector; + int status; + + /* It is assumed the sentinel always exists. */ + vectors = talloc_array(tracee->ctx, ElfAuxVector, 1); + if (vectors == NULL) + return NULL; + vectors[0].type = AT_NULL; + vectors[0].value = 0; + + while (1) { + vector.type = peek_word(tracee, address); + if (errno != 0) + return NULL; + address += sizeof_word(tracee); + + if (vector.type == AT_NULL) + break; /* Already added. */ + + vector.value = peek_word(tracee, address); + if (errno != 0) + return NULL; + address += sizeof_word(tracee); + + status = add_elf_aux_vector(&vectors, vector.type, vector.value); + if (status < 0) + return NULL; + } + + return vectors; +} diff --git a/app/src/main/cpp/proot/src/execve/auxv.h b/app/src/main/cpp/proot/src/execve/auxv.h new file mode 100644 index 0000000..8dd59e7 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/auxv.h @@ -0,0 +1,38 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef AUXV +#define AUXV + +#include "tracee/tracee.h" +#include "arch.h" + +typedef struct elf_aux_vector { + word_t type; + word_t value; +} ElfAuxVector; + +extern word_t get_elf_aux_vectors_address(const Tracee *tracee); +extern ElfAuxVector *fetch_elf_aux_vectors(const Tracee *tracee, word_t address); +extern int add_elf_aux_vector(ElfAuxVector **vectors, word_t type, word_t value); + +#endif /* AUXV */ diff --git a/app/src/main/cpp/proot/src/execve/elf.c b/app/src/main/cpp/proot/src/execve/elf.c new file mode 100644 index 0000000..88fe150 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/elf.c @@ -0,0 +1,144 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* open(2), */ +#include /* read(2), close(2), */ +#include /* EACCES, ENOTSUP, */ +#include /* UINT64_MAX, */ +#include /* PATH_MAX, */ +#include /* str*(3), memcpy(3), */ +#include /* assert(3), */ +#include /* talloc_*, */ +#include /* bool, true, false, */ + +#include "execve/elf.h" +#include "tracee/tracee.h" +#include "cli/note.h" +#include "arch.h" + +#include "compat.h" + +/** + * Open the ELF file @t_path and extract its header into @elf_header. + * This function returns -errno if an error occured, otherwise the + * file descriptor for @t_path. + */ +int open_elf(const char *t_path, ElfHeader *elf_header) +{ + int fd; + int status; + + /* + * Read the ELF header. + */ + + fd = open(t_path, O_RDONLY); + if (fd < 0) + return -errno; + + /* Check if it is an ELF file. */ + status = read(fd, elf_header, sizeof(ElfHeader)); + if (status < 0) { + status = -errno; + goto end; + } + if ((size_t) status < sizeof(ElfHeader) + || ELF_IDENT(*elf_header, 0) != 0x7f + || ELF_IDENT(*elf_header, 1) != 'E' + || ELF_IDENT(*elf_header, 2) != 'L' + || ELF_IDENT(*elf_header, 3) != 'F') { + status = -ENOEXEC; + goto end; + } + + /* Check if it is a known class (32-bit or 64-bit). */ + if ( !IS_CLASS32(*elf_header) + && !IS_CLASS64(*elf_header)) { + status = -ENOEXEC; + goto end; + } + + status = 0; +end: + /* Delayed error handling. */ + if (status < 0) { + close(fd); + return status; + } + + return fd; +} + +/** + * Invoke @callback(..., @data) for each program headers from the + * specified ELF file (referenced by @fd, with the given @elf_header). + * This function returns -errno if an error occured, or it returns + * immediately the value != 0 returned by @callback, otherwise 0. + */ +int iterate_program_headers(const Tracee *tracee, int fd, const ElfHeader *elf_header, + program_headers_iterator_t callback, void *data) +{ + ProgramHeader program_header; + + uint64_t elf_phoff; + uint16_t elf_phentsize; + uint16_t elf_phnum; + + int status; + int i; + + /* Get class-specific fields. */ + elf_phnum = ELF_FIELD(*elf_header, phnum); + elf_phentsize = ELF_FIELD(*elf_header, phentsize); + elf_phoff = ELF_FIELD(*elf_header, phoff); + + /* + * Some sanity checks regarding the current + * support of the ELF specification in PRoot. + */ + + if (elf_phnum >= 0xffff) { + note(tracee, WARNING, INTERNAL, "%d: big PH tables are not yet supported.", fd); + return -ENOTSUP; + } + + if (!KNOWN_PHENTSIZE(*elf_header, elf_phentsize)) { + note(tracee, WARNING, INTERNAL, "%d: unsupported size of program header.", fd); + return -ENOTSUP; + } + + status = (int) lseek(fd, elf_phoff, SEEK_SET); + if (status < 0) + return -errno; + + for (i = 0; i < elf_phnum; i++) { + status = read(fd, &program_header, elf_phentsize); + if (status != elf_phentsize) + return (status < 0 ? -errno : -ENOTSUP); + + status = callback(elf_header, &program_header, data); + if (status != 0) + return status; + } + + return 0; +} diff --git a/app/src/main/cpp/proot/src/execve/elf.h b/app/src/main/cpp/proot/src/execve/elf.h new file mode 100644 index 0000000..a6251e8 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/elf.h @@ -0,0 +1,162 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef ELF_H +#define ELF_H + +#define EI_NIDENT 16 + +#include +#include + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} ElfHeader32; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint64_t e_entry; + uint64_t e_phoff; + uint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +} ElfHeader64; + +typedef union { + ElfHeader32 class32; + ElfHeader64 class64; +} ElfHeader; + +typedef struct { + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +} ProgramHeader32; + +typedef struct { + uint32_t p_type; + uint32_t p_flags; + uint64_t p_offset; + uint64_t p_vaddr; + uint64_t p_paddr; + uint64_t p_filesz; + uint64_t p_memsz; + uint64_t p_align; +} ProgramHeader64; + +typedef union { + ProgramHeader32 class32; + ProgramHeader64 class64; +} ProgramHeader; + +/* Object type: */ +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 + +/* Segment flags: */ +#define PF_X 1 +#define PF_W 2 +#define PF_R 4 + +typedef enum { + PT_LOAD = 1, + PT_DYNAMIC = 2, + PT_INTERP = 3, + PT_GNU_STACK = 0x6474e551, +} SegmentType; + +typedef enum { + DT_STRTAB = 5, + DT_RPATH = 15, + DT_RUNPATH = 29 +} DynamicType; + +/* The following macros are also compatible with ELF 64-bit. */ +#define ELF_IDENT(header, index) (header).class32.e_ident[(index)] +#define ELF_CLASS(header) ELF_IDENT(header, 4) +#define IS_CLASS32(header) (ELF_CLASS(header) == 1) +#define IS_CLASS64(header) (ELF_CLASS(header) == 2) + +/* Helper to access a @field of the structure ElfHeaderXX. */ +#define ELF_FIELD(header, field) \ + (IS_CLASS64(header) \ + ? (header).class64. e_ ## field \ + : (header).class32. e_ ## field) + +/* Helper to access a @field of the structure ProgramHeaderXX */ +#define PROGRAM_FIELD(ehdr, phdr, field) \ + (IS_CLASS64(ehdr) \ + ? (phdr).class64. p_ ## field \ + : (phdr).class32. p_ ## field) + +/* Helper to access a @field of the structure DynamicEntryXX */ +#define DYNAMIC_FIELD(ehdr, dynent, field) \ + (IS_CLASS64(ehdr) \ + ? (dynent).class64. d_ ## field \ + : (dynent).class32. d_ ## field) + +#define KNOWN_PHENTSIZE(header, size) \ + ( (IS_CLASS32(header) && (size) == sizeof(ProgramHeader32)) \ + || (IS_CLASS64(header) && (size) == sizeof(ProgramHeader64))) + +#define IS_POSITION_INDENPENDANT(elf_header) \ + (ELF_FIELD((elf_header), type) == ET_DYN) + +#include "tracee/tracee.h" + +extern int open_elf(const char *t_path, ElfHeader *elf_header); + +typedef int (* program_headers_iterator_t)(const ElfHeader *elf_header, + const ProgramHeader *program_header, void *data); + +extern int iterate_program_headers(const Tracee *tracee, int fd, const ElfHeader *elf_header, + program_headers_iterator_t callback, void *data); + +#endif /* ELF_H */ diff --git a/app/src/main/cpp/proot/src/execve/enter.c b/app/src/main/cpp/proot/src/execve/enter.c new file mode 100644 index 0000000..43c7666 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/enter.c @@ -0,0 +1,464 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* lstat(2), lseek(2), */ +#include /* lstat(2), lseek(2), fchmod(2), */ +#include /* access(2), lstat(2), close(2), read(2), */ +#include /* E*, */ +#include /* assert(3), */ +#include /* talloc*, */ +#include /* PROT_*, */ +#include /* strlen(3), strcpy(3), */ +#include /* getenv(3), */ +#include /* fwrite(3), */ +#include /* assert(3), */ + +#include "execve/execve.h" +#include "execve/shebang.h" +#include "execve/aoxp.h" +#include "execve/elf.h" +#include "path/path.h" +#include "path/temp.h" +#include "path/binding.h" +#include "tracee/tracee.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "arch.h" +#include "cli/note.h" + +#define P(a) PROGRAM_FIELD(load_info->elf_header, *program_header, a) + +/** + * Add @program_header (type PT_LOAD) to @load_info->mappings. This + * function returns -errno if an error occured, otherwise it returns + * 0. + */ +static int add_mapping(const Tracee *tracee UNUSED, LoadInfo *load_info, + const ProgramHeader *program_header) +{ + size_t index; + word_t start_address; + word_t end_address; + static word_t page_size = 0; + static word_t page_mask = 0; + + if (page_size == 0) { + page_size = sysconf(_SC_PAGE_SIZE); + if ((int) page_size <= 0) + page_size = 0x1000; + page_mask = ~(page_size - 1); + } + + if (load_info->mappings == NULL) + index = 0; + else + index = talloc_array_length(load_info->mappings); + + load_info->mappings = talloc_realloc(load_info, load_info->mappings, Mapping, index + 1); + if (load_info->mappings == NULL) + return -ENOMEM; + + start_address = P(vaddr) & page_mask; + end_address = (P(vaddr) + P(filesz) + page_size) & page_mask; + + load_info->mappings[index].fd = -1; /* Unknown yet. */ + load_info->mappings[index].offset = P(offset) & page_mask; + load_info->mappings[index].addr = start_address; + load_info->mappings[index].length = end_address - start_address; + load_info->mappings[index].flags = MAP_PRIVATE | MAP_FIXED; + load_info->mappings[index].prot = ( (P(flags) & PF_R ? PROT_READ : 0) + | (P(flags) & PF_W ? PROT_WRITE : 0) + | (P(flags) & PF_X ? PROT_EXEC : 0)); + + /* "If the segment's memory size p_memsz is larger than the + * file size p_filesz, the "extra" bytes are defined to hold + * the value 0 and to follow the segment's initialized area." + * -- man 7 elf. */ + if (P(memsz) > P(filesz)) { + /* How many extra bytes in the current page? */ + load_info->mappings[index].clear_length = end_address - P(vaddr) - P(filesz); + + /* Create new pages for the remaining extra bytes. */ + start_address = end_address; + end_address = (P(vaddr) + P(memsz) + page_size) & page_mask; + if (end_address > start_address) { + index++; + load_info->mappings = talloc_realloc(load_info, load_info->mappings, + Mapping, index + 1); + if (load_info->mappings == NULL) + return -ENOMEM; + + load_info->mappings[index].fd = -1; /* Anonymous. */ + load_info->mappings[index].offset = 0; + load_info->mappings[index].addr = start_address; + load_info->mappings[index].length = end_address - start_address; + load_info->mappings[index].clear_length = 0; + load_info->mappings[index].flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + load_info->mappings[index].prot = load_info->mappings[index - 1].prot; + } + } + else + load_info->mappings[index].clear_length = 0; + + return 0; +} + +/** + * Translate @user_path into @host_path and check if this latter exists, is + * executable and is a regular file. This function returns -errno if + * an error occured, 0 otherwise. + */ +int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path) +{ + struct stat statl; + int status; + + if (user_path[0] == '\0') + return -ENOEXEC; + + status = translate_path(tracee, host_path, AT_FDCWD, user_path, true); + if (status < 0) + return status; + + status = access(host_path, F_OK); + if (status < 0) + return -ENOENT; + + status = access(host_path, X_OK); + if (status < 0) + return -EACCES; + + status = lstat(host_path, &statl); + if (status < 0) + return -EPERM; + + return 0; +} + +/** + * Add @program_header (type PT_INTERP) to @load_info->interp. This + * function returns -errno if an error occured, otherwise it returns + * 0. + */ +static int add_interp(Tracee *tracee, int fd, LoadInfo *load_info, + const ProgramHeader *program_header) +{ + char host_path[PATH_MAX]; + char *user_path; + int status; + + /* Only one PT_INTERP segment is allowed. */ + if (load_info->interp != NULL) + return -EINVAL; + + load_info->interp = talloc_zero(load_info, LoadInfo); + if (load_info->interp == NULL) + return -ENOMEM; + + user_path = talloc_size(tracee->ctx, P(filesz) + 1); + if (user_path == NULL) + return -ENOMEM; + + /* Remember pread(2) doesn't change the + * current position in the file. */ + status = pread(fd, user_path, P(filesz), P(offset)); + if ((size_t) status != P(filesz)) /* Unexpected size. */ + status = -EACCES; + if (status < 0) + return status; + + user_path[P(filesz)] = '\0'; + + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + load_info->interp->host_path = talloc_strdup(load_info->interp, host_path); + if (load_info->interp->host_path == NULL) + return -ENOMEM; + + load_info->interp->user_path = talloc_strdup(load_info->interp, user_path); + if (load_info->interp->user_path == NULL) + return -ENOMEM; + + return 0; +} + +#undef P + +struct add_load_info_data { + LoadInfo *load_info; + Tracee *tracee; + int fd; +}; + +/** + * This function is a program header iterator. It invokes + * add_mapping() or add_interp(), according to the type of + * @program_header. This function returns -errno if an error + * occurred, otherwise 0. + */ +static int add_load_info(const ElfHeader *elf_header, + const ProgramHeader *program_header, void *data_) +{ + struct add_load_info_data *data = data_; + int status; + + switch (PROGRAM_FIELD(*elf_header, *program_header, type)) { + case PT_LOAD: + status = add_mapping(data->tracee, data->load_info, program_header); + if (status < 0) + return status; + break; + + case PT_INTERP: + status = add_interp(data->tracee, data->fd, data->load_info, program_header); + if (status < 0) + return status; + break; + + case PT_GNU_STACK: + data->load_info->needs_executable_stack |= + ((PROGRAM_FIELD(*elf_header, *program_header, flags) & PF_X) != 0); + break; + + default: + break; + } + + return 0; +} + +/** + * Extract the load info from @load->host_path. This function returns + * -errno if an error occured, otherwise it returns 0. + */ +static int extract_load_info(Tracee *tracee, LoadInfo *load_info) +{ + struct add_load_info_data data; + int fd = -1; + int status; + + assert(load_info != NULL); + assert(load_info->host_path != NULL); + + fd = open_elf(load_info->host_path, &load_info->elf_header); + if (fd < 0) + return fd; + + /* Sanity check. */ + switch (ELF_FIELD(load_info->elf_header, type)) { + case ET_EXEC: + case ET_DYN: + break; + + default: + status = -EINVAL; + goto end; + } + + data.load_info = load_info; + data.tracee = tracee; + data.fd = fd; + + status = iterate_program_headers(tracee, fd, &load_info->elf_header, add_load_info, &data); +end: + if (fd >= 0) + close(fd); + + return status; +} + +/** + * Add @load_base to each adresses of @load_info. + */ +static void add_load_base(LoadInfo *load_info, word_t load_base) +{ + size_t nb_mappings; + size_t i; + + nb_mappings = talloc_array_length(load_info->mappings); + for (i = 0; i < nb_mappings; i++) + load_info->mappings[i].addr += load_base; + + if (IS_CLASS64(load_info->elf_header)) + load_info->elf_header.class64.e_entry += load_base; + else + load_info->elf_header.class32.e_entry += load_base; +} + +/** + * Compute the final load address for each position independant + * objects of @tracee. + * + */ +static void compute_load_addresses(Tracee *tracee) +{ + if (IS_POSITION_INDENPENDANT(tracee->load_info->elf_header) + && tracee->load_info->mappings[0].addr == 0) { +#if defined(HAS_LOADER_32BIT) + if (IS_CLASS32(tracee->load_info->elf_header)) + add_load_base(tracee->load_info, EXEC_PIC_ADDRESS_32); + else +#endif + add_load_base(tracee->load_info, EXEC_PIC_ADDRESS); + } + + /* Nothing more to do? */ + if (tracee->load_info->interp == NULL) + return; + + if (IS_POSITION_INDENPENDANT(tracee->load_info->interp->elf_header) + && tracee->load_info->interp->mappings[0].addr == 0) { +#if defined(HAS_LOADER_32BIT) + if (IS_CLASS32(tracee->load_info->elf_header)) + add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS_32); + else +#endif + add_load_base(tracee->load_info->interp, INTERP_PIC_ADDRESS); + } +} + +/** + * Get the path to the loader for the given @tracee. This function + * returns NULL if an error occurred. + */ +static inline const char *get_loader_path(const Tracee *tracee) +{ + static char *loader_path = NULL; + + if (IS_CLASS32(tracee->load_info->elf_header)) + loader_path = getenv("PROOT_LOADER_32"); + else + loader_path = getenv("PROOT_LOADER"); + + return loader_path; +} + +/** + * Extract all the information that will be required by + * translate_load_*(). This function returns -errno if an error + * occured, otherwise 0. + */ +int translate_execve_enter(Tracee *tracee) +{ + char user_path[PATH_MAX]; + char host_path[PATH_MAX]; + char new_exe[PATH_MAX]; + char *raw_path; + const char *loader_path; + int status; + + if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) { + /* Syscalls can now be reported to its ptracer. */ + tracee->as_ptracee.ignore_loader_syscalls = false; + + /* Cancel this spurious execve, it was only used as a + * notification. */ + set_sysnum(tracee, PR_void); + return 0; + } + + status = get_sysarg_path(tracee, user_path, SYSARG_1); + if (status < 0) + return status; + + /* Remember the user path before it is overwritten by + * expand_shebang(). This "raw" path is useful to fix the + * value of AT_EXECFN and /proc/{@tracee->pid}/comm. */ + raw_path = talloc_strdup(tracee->ctx, user_path); + if (raw_path == NULL) + return -ENOMEM; + + status = expand_shebang(tracee, host_path, user_path); + if (status < 0) + /* The Linux kernel actually returns -EACCES when + * trying to execute a directory. */ + return status == -EISDIR ? -EACCES : status; + + /* user_path is modified only if there's an interpreter + * (ie. for a script). */ + if (status == 0) + TALLOC_FREE(raw_path); + + strcpy(new_exe, host_path); + status = detranslate_path(tracee, new_exe, NULL); + if (status >= 0) { + talloc_unlink(tracee, tracee->new_exe); + tracee->new_exe = talloc_strdup(tracee, new_exe); + } + else + tracee->new_exe = NULL; + + talloc_unlink(tracee, tracee->load_info); + + tracee->load_info = talloc_zero(tracee, LoadInfo); + if (tracee->load_info == NULL) + return -ENOMEM; + + tracee->load_info->host_path = talloc_strdup(tracee->load_info, host_path); + if (tracee->load_info->host_path == NULL) + return -ENOMEM; + + tracee->load_info->user_path = talloc_strdup(tracee->load_info, user_path); + if (tracee->load_info->user_path == NULL) + return -ENOMEM; + + tracee->load_info->raw_path = (raw_path != NULL + ? talloc_reparent(tracee->ctx, tracee->load_info, raw_path) + : talloc_reference(tracee->load_info, tracee->load_info->user_path)); + if (tracee->load_info->raw_path == NULL) + return -ENOMEM; + + status = extract_load_info(tracee, tracee->load_info); + if (status < 0) + return status; + + if (tracee->load_info->interp != NULL) { + status = extract_load_info(tracee, tracee->load_info->interp); + if (status < 0) + return status; + + /* An ELF interpreter is supposed to be + * standalone. */ + if (tracee->load_info->interp->interp != NULL) { + TALLOC_FREE(tracee->load_info->interp->interp); + return -EINVAL; + } + } + + compute_load_addresses(tracee); + + /* Execute the loader instead of the program. */ + loader_path = get_loader_path(tracee); + if (loader_path == NULL) + return -ENOENT; + + status = set_sysarg_path(tracee, loader_path, SYSARG_1); + if (status < 0) + return status; + + /* Mask to its ptracer syscalls performed by the loader. */ + tracee->as_ptracee.ignore_loader_syscalls = true; + + return 0; +} diff --git a/app/src/main/cpp/proot/src/execve/execve.h b/app/src/main/cpp/proot/src/execve/execve.h new file mode 100644 index 0000000..4a1a409 --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/execve.h @@ -0,0 +1,64 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef EXECVE_H +#define EXECVE_H + +#include /* PATH_MAX, */ + +#include "tracee/tracee.h" +#include "execve/elf.h" +#include "arch.h" + +extern int translate_execve_enter(Tracee *tracee); +extern void translate_execve_exit(Tracee *tracee); +extern int translate_and_check_exec(Tracee *tracee, char host_path[PATH_MAX], const char *user_path); + +typedef struct mapping { + word_t addr; + word_t length; + word_t clear_length; + word_t prot; + word_t flags; + word_t fd; + word_t offset; +} Mapping; + +typedef struct load_info { + char *host_path; + char *user_path; + char *raw_path; + Mapping *mappings; + ElfHeader elf_header; + bool needs_executable_stack; + + struct load_info *interp; +} LoadInfo; + +#define IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee) ( \ + (tracee)->as_ptracee.ptracer != NULL \ + && peek_reg((tracee), ORIGINAL, SYSARG_1) == (word_t) 1 \ + && peek_reg((tracee), ORIGINAL, SYSARG_4) == (word_t) 2 \ + && peek_reg((tracee), ORIGINAL, SYSARG_5) == (word_t) 3 \ + && peek_reg((tracee), ORIGINAL, SYSARG_6) == (word_t) 4) + +#endif /* EXECVE_H */ diff --git a/app/src/main/cpp/proot/src/execve/exit.c b/app/src/main/cpp/proot/src/execve/exit.c new file mode 100644 index 0000000..bf3ce1a --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/exit.c @@ -0,0 +1,487 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* AT_*, */ +#include /* talloc*, */ +#include /* MAP_*, */ +#include /* assert(3), */ +#include /* strlen(3), strerror(3), */ +#include /* bzero(3), */ +#include /* kill(2), SIG*, */ +#include /* write(2), */ +#include /* E*, */ + +#include "execve/execve.h" +#include "execve/elf.h" +#include "loader/script.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "tracee/mem.h" +#include "syscall/sysnum.h" +#include "execve/auxv.h" +#include "path/binding.h" +#include "path/temp.h" +#include "cli/note.h" + + +/** + * Fill @path with the content of @vectors, formatted according to + * @ptracee's current ABI. + */ +static int fill_file_with_auxv(const Tracee *ptracee, const char *path, + const ElfAuxVector *vectors) +{ + const ssize_t current_sizeof_word = sizeof_word(ptracee); + ssize_t status; + int fd = -1; + int i; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + i = 0; + do { + status = write(fd, &vectors[i].type, current_sizeof_word); + if (status < current_sizeof_word) { + status = -1; + goto end; + } + + status = write(fd, &vectors[i].value, current_sizeof_word); + if (status < current_sizeof_word) { + status = -1; + goto end; + } + } while (vectors[i++].type != AT_NULL); + + status = 0; +end: + if (fd >= 0) + (void) close(fd); + + return status; +} + +/** + * Bind content of @vectors over /proc/{@ptracee->pid}/auxv. This + * function returns -1 if an error occurred, otherwise 0. + */ +static int bind_proc_pid_auxv(const Tracee *ptracee) +{ + word_t vectors_address; + ElfAuxVector *vectors; + + const char *guest_path; + const char *host_path; + Binding *binding; + int status; + + vectors_address = get_elf_aux_vectors_address(ptracee); + if (vectors_address == 0) + return -1; + + vectors = fetch_elf_aux_vectors(ptracee, vectors_address); + if (vectors == NULL) + return -1; + + /* Path to these ELF auxiliary vectors. */ + guest_path = talloc_asprintf(ptracee->ctx, "/proc/%d/auxv", ptracee->pid); + if (guest_path == NULL) + return -1; + + /* Remove binding to this path, if any. It contains ELF + * auxiliary vectors of the previous execve(2). */ + binding = get_binding(ptracee, GUEST, guest_path); + if (binding != NULL && compare_paths(binding->guest.path, guest_path) == PATHS_ARE_EQUAL) { + remove_binding_from_all_lists(ptracee, binding); + TALLOC_FREE(binding); + } + + host_path = create_temp_file(ptracee->ctx, "auxv"); + if (host_path == NULL) + return -1; + + status = fill_file_with_auxv(ptracee, host_path, vectors); + if (status < 0) + return -1; + + /* Note: this binding will be removed once ptracee gets freed. */ + binding = insort_binding3(ptracee, ptracee->life_context, host_path, guest_path); + if (binding == NULL) + return -1; + + /* This temporary file (host_path) will be removed once the + * binding is freed. */ + talloc_reparent(ptracee->ctx, binding, host_path); + + return 0; +} + +/** + * Convert @mappings into load @script statements at the given @cursor + * position. This function returns the new cursor position. + */ +static void *transcript_mappings(void *cursor, const Mapping *mappings) +{ + size_t nb_mappings; + size_t i; + + nb_mappings = talloc_array_length(mappings); + for (i = 0; i < nb_mappings; i++) { + LoadStatement *statement = cursor; + + if ((mappings[i].flags & MAP_ANONYMOUS) != 0) + statement->action = LOAD_ACTION_MMAP_ANON; + else + statement->action = LOAD_ACTION_MMAP_FILE; + + statement->mmap.addr = mappings[i].addr; + statement->mmap.length = mappings[i].length; + statement->mmap.prot = mappings[i].prot; + statement->mmap.offset = mappings[i].offset; + statement->mmap.clear_length = mappings[i].clear_length; + + cursor += LOAD_STATEMENT_SIZE(*statement, mmap); + } + + return cursor; +} + +/** + * Convert @tracee->load_info into a load script, then transfer this + * latter into @tracee's memory. + */ +static int transfer_load_script(Tracee *tracee) +{ + const word_t stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); + static word_t page_size = 0; + static word_t page_mask = 0; + + word_t entry_point; + + size_t script_size; + size_t strings_size; + size_t string1_size; + size_t string2_size; + size_t string3_size; + size_t padding_size; + + word_t string1_address; + word_t string2_address; + word_t string3_address; + + void *buffer; + size_t buffer_size; + + bool needs_executable_stack; + LoadStatement *statement; + void *cursor; + int status; + + if (page_size == 0) { + page_size = sysconf(_SC_PAGE_SIZE); + if ((int) page_size <= 0) + page_size = 0x1000; + page_mask = ~(page_size - 1); + } + + needs_executable_stack = (tracee->load_info->needs_executable_stack + || ( tracee->load_info->interp != NULL + && tracee->load_info->interp->needs_executable_stack)); + + /* Strings addresses are required to generate the load script, + * for "open" actions. Since I want to generate it in one + * pass, these strings will be put right below the current + * stack pointer -- the only known adresses so far -- in the + * "strings area". */ + string1_size = strlen(tracee->load_info->user_path) + 1; + + string2_size = (tracee->load_info->interp == NULL ? 0 + : strlen(tracee->load_info->interp->user_path) + 1); + + string3_size = (tracee->load_info->raw_path == tracee->load_info->user_path ? 0 + : strlen(tracee->load_info->raw_path) + 1); + + /* A padding will be appended at the end of the load script + * (a.k.a "strings area") to ensure this latter is aligned properly. */ + padding_size = (stack_pointer - string1_size - string2_size - string3_size) + % STACK_ALIGNMENT; + + strings_size = string1_size + string2_size + string3_size + padding_size; + string1_address = stack_pointer - strings_size; + string2_address = stack_pointer - strings_size + string1_size; + string3_address = (string3_size == 0 + ? string1_address + : stack_pointer - strings_size + string1_size + string2_size); + + /* Compute the size of the load script. */ + script_size = + LOAD_STATEMENT_SIZE(*statement, open) + + (LOAD_STATEMENT_SIZE(*statement, mmap) + * talloc_array_length(tracee->load_info->mappings)) + + (tracee->load_info->interp == NULL ? 0 + : LOAD_STATEMENT_SIZE(*statement, open) + + (LOAD_STATEMENT_SIZE(*statement, mmap) + * talloc_array_length(tracee->load_info->interp->mappings))) + + (needs_executable_stack ? LOAD_STATEMENT_SIZE(*statement, make_stack_exec) : 0) + + LOAD_STATEMENT_SIZE(*statement, start); + + /* Allocate enough room for both the load script and the + * strings area. */ + buffer_size = script_size + strings_size; + buffer = talloc_zero_size(tracee->ctx, buffer_size); + if (buffer == NULL) + return -ENOMEM; + + cursor = buffer; + + /* Load script statement: open. */ + statement = cursor; + statement->action = LOAD_ACTION_OPEN; + statement->open.string_address = string1_address; + + cursor += LOAD_STATEMENT_SIZE(*statement, open); + + /* Load script statements: mmap. */ + cursor = transcript_mappings(cursor, tracee->load_info->mappings); + + if (tracee->load_info->interp != NULL) { + /* Load script statement: open. */ + statement = cursor; + statement->action = LOAD_ACTION_OPEN_NEXT; + statement->open.string_address = string2_address; + + cursor += LOAD_STATEMENT_SIZE(*statement, open); + + /* Load script statements: mmap. */ + cursor = transcript_mappings(cursor, tracee->load_info->interp->mappings); + + entry_point = ELF_FIELD(tracee->load_info->interp->elf_header, entry); + } + else + entry_point = ELF_FIELD(tracee->load_info->elf_header, entry); + + if (needs_executable_stack) { + /* Load script statement: stack_exec. */ + statement = cursor; + + statement->action = LOAD_ACTION_MAKE_STACK_EXEC; + statement->make_stack_exec.start = stack_pointer & page_mask; + + cursor += LOAD_STATEMENT_SIZE(*statement, make_stack_exec); + } + + /* Load script statement: start. */ + statement = cursor; + + /* Start of the program slightly differs when ptraced. */ + if (tracee->as_ptracee.ptracer != NULL) + statement->action = LOAD_ACTION_START_TRACED; + else + statement->action = LOAD_ACTION_START; + + statement->start.stack_pointer = stack_pointer; + statement->start.entry_point = entry_point; + statement->start.at_phent = ELF_FIELD(tracee->load_info->elf_header, phentsize); + statement->start.at_phnum = ELF_FIELD(tracee->load_info->elf_header, phnum); + statement->start.at_entry = ELF_FIELD(tracee->load_info->elf_header, entry); + statement->start.at_phdr = ELF_FIELD(tracee->load_info->elf_header, phoff) + + tracee->load_info->mappings[0].addr; + statement->start.at_execfn = string3_address; + + cursor += LOAD_STATEMENT_SIZE(*statement, start); + + /* Sanity check. */ + assert((uintptr_t) cursor - (uintptr_t) buffer == script_size); + + /* Convert the load script to the expected format. */ + if (is_32on64_mode(tracee)) { + int i; + for (i = 0; buffer + i * sizeof(uint64_t) < cursor; i++) + ((uint32_t *) buffer)[i] = ((uint64_t *) buffer)[i]; + } + + /* Concatenate the load script and the strings. */ + memcpy(cursor, tracee->load_info->user_path, string1_size); + cursor += string1_size; + + if (string2_size != 0) { + memcpy(cursor, tracee->load_info->interp->user_path, string2_size); + cursor += string2_size; + } + + if (string3_size != 0) { + memcpy(cursor, tracee->load_info->raw_path, string3_size); + cursor += string3_size; + } + + /* Sanity check. */ + cursor += padding_size; + assert((uintptr_t) cursor - (uintptr_t) buffer == buffer_size); + + /* Allocate enough room in tracee's memory for the load + * script, and make the first user argument points to this + * location. Note that it is safe to update the stack pointer + * manually since we are in execve sysexit. However it should + * be done before transfering data since the kernel might not + * allow page faults below the stack pointer. */ + poke_reg(tracee, STACK_POINTER, stack_pointer - buffer_size); + poke_reg(tracee, USERARG_1, stack_pointer - buffer_size); + + /* Copy everything in the tracee's memory at once. */ + status = write_data(tracee, stack_pointer - buffer_size, buffer, buffer_size); + if (status < 0) + return status; + + /* Tracee's stack content is now as follow: + * + * +------------+ <- initial stack pointer (higher address) + * | padding | + * +------------+ + * | string3 | + * +------------+ + * | string2 | + * +------------+ + * | string1 | + * +------------+ + * | start | + * +------------+ + * | mmap anon | + * +------------+ + * | mmap file | + * +------------+ + * | open next | + * +------------+ + * | mmap anon. | + * +------------+ + * | mmap file | + * +------------+ + * | open | + * +------------+ <- stack pointer, userarg1 (word aligned) + */ + + /* Remember we are in the sysexit stage, so be sure the + * current register values will be used as-is at the end. */ + save_current_regs(tracee, ORIGINAL); + tracee->_regs_were_changed = true; + + return 0; +} + +/** + * Start the loading of @tracee. This function returns no error since + * it's either too late to do anything useful (the calling process is + * already replaced) or the error reported by the kernel + * (syscall_result < 0) will be propagated as-is. + */ +void translate_execve_exit(Tracee *tracee) +{ + word_t syscall_result; + int status; + + if (IS_NOTIFICATION_PTRACED_LOAD_DONE(tracee)) { + /* Be sure not to confuse the ptracer with an + * unexpected syscall/returned value. */ + poke_reg(tracee, SYSARG_RESULT, 0); + set_sysnum(tracee, PR_execve); + + /* According to most ABIs, all registers have + * undefined values at program startup except: + * + * - the stack pointer + * - the instruction pointer + * - the rtld_fini pointer + * - the state flags + */ + poke_reg(tracee, STACK_POINTER, peek_reg(tracee, ORIGINAL, SYSARG_2)); + poke_reg(tracee, INSTR_POINTER, peek_reg(tracee, ORIGINAL, SYSARG_3)); + poke_reg(tracee, RTLD_FINI, 0); + poke_reg(tracee, STATE_FLAGS, 0); + +#if defined(ARCH_ARM_EABI) && defined(__thumb__) + /* Leave ARM thumb mode */ + tracee->_regs[CURRENT].ARM_cpsr &= ~PSR_T_BIT; +#endif + + /* Restore registers to their current values. */ + save_current_regs(tracee, ORIGINAL); + tracee->_regs_were_changed = true; + + /* This is is required to make GDB work correctly + * under PRoot, however it deserves to be used + * unconditionally. */ + (void) bind_proc_pid_auxv(tracee); + + /* If the PTRACE_O_TRACEEXEC option is *not* in effect + * for the execing tracee, the kernel delivers an + * extra SIGTRAP to the tracee after execve(2) + * *returns*. This is an ordinary signal (similar to + * one which can be generated by "kill -TRAP"), not a + * special kind of ptrace-stop. Employing + * PTRACE_GETSIGINFO for this signal returns si_code + * set to 0 (SI_USER). This signal may be blocked by + * signal mask, and thus may be delivered (much) + * later. -- man 2 ptrace + * + * This signal is delayed so far since the program was + * not fully loaded yet; GDB would get "invalid + * adress" errors otherwise. */ + if ((tracee->as_ptracee.options & PTRACE_O_TRACEEXEC) == 0) + kill(tracee->pid, SIGTRAP); + + return; + } + +#ifdef ARCH_ARM64 + tracee->is_aarch32 = IS_CLASS32(tracee->load_info->elf_header); +#endif + + syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + if ((int) syscall_result < 0) + return; + + /* Execve happened; commit the new "/proc/self/exe". */ + if (tracee->new_exe != NULL) { + (void) talloc_unlink(tracee, tracee->exe); + tracee->exe = talloc_reference(tracee, tracee->new_exe); + talloc_set_name_const(tracee->exe, "$exe"); + } + + /* New processes have no heap. The process could've been cloned with + * CLONE_VM so it has been sharing the heap with its parent. execve() + * discards the VM so make sure to reallocate new heap. */ + if (talloc_reference_count(tracee->heap) > 0) { + talloc_unlink(tracee, tracee->heap); + tracee->heap = talloc_zero(tracee, Heap); + if (!tracee->heap) + note(tracee, ERROR, INTERNAL, "can't allocate heap"); + } else { + bzero(tracee->heap, sizeof(Heap)); + } + + status = transfer_load_script(tracee); + if (status < 0) + note(tracee, ERROR, INTERNAL, "can't transfer load script: %s", strerror(-status)); + + return; +} diff --git a/app/src/main/cpp/proot/src/execve/shebang.c b/app/src/main/cpp/proot/src/execve/shebang.c new file mode 100644 index 0000000..83ceeda --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/shebang.c @@ -0,0 +1,307 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* open(2), */ +#include /* open(2), */ +#include /* open(2), */ +#include /* PATH_MAX, */ +#include /* BINPRM_BUF_SIZE, */ +#include /* read(2), close(2), */ +#include /* -E*, */ +#include /* MAXSYMLINKS, */ +#include /* bool, */ +#include /* assert(3), */ + +#include "execve/shebang.h" +#include "execve/execve.h" +#include "execve/aoxp.h" +#include "tracee/tracee.h" +#include "attribute.h" + +/** + * Extract into @user_path and @argument the shebang from @host_path. + * This function returns -errno if an error occured, 1 if a shebang + * was found and extracted, otherwise 0. + * + * Extract from "man 2 execve": + * + * On Linux, the entire string following the interpreter name is + * passed as a *single* argument to the interpreter, and this + * string can include white space. + */ +static int extract_shebang(const Tracee *tracee UNUSED, const char *host_path, + char user_path[PATH_MAX], char argument[BINPRM_BUF_SIZE]) +{ + char tmp2[2]; + char tmp; + + size_t current_length; + size_t i; + + int status; + int fd; + + /* Assumption. */ + assert(BINPRM_BUF_SIZE < PATH_MAX); + + argument[0] = '\0'; + + /* Inspect the executable. */ + fd = open(host_path, O_RDONLY); + if (fd < 0) + return -errno; + + status = read(fd, tmp2, 2 * sizeof(char)); + if (status < 0) { + status = -errno; + goto end; + } + if ((size_t) status < 2 * sizeof(char)) { /* EOF */ + status = 0; + goto end; + } + + /* Check if it really is a script text. */ + if (tmp2[0] != '#' || tmp2[1] != '!') { + status = 0; + goto end; + } + current_length = 2; + user_path[0] = '\0'; + + /* Skip leading spaces. */ + do { + status = read(fd, &tmp, sizeof(char)); + if (status < 0) { + status = -errno; + goto end; + } + if ((size_t) status < sizeof(char)) { /* EOF */ + status = -ENOEXEC; + goto end; + } + + current_length++; + } while ((tmp == ' ' || tmp == '\t') && current_length < BINPRM_BUF_SIZE); + + /* Slurp the interpreter path until the first space or end-of-line. */ + for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) { + switch (tmp) { + case ' ': + case '\t': + /* Remove spaces in between the interpreter + * and the hypothetical argument. */ + user_path[i] = '\0'; + break; + + case '\n': + case '\r': + /* There is no argument. */ + user_path[i] = '\0'; + argument[0] = '\0'; + status = 1; + goto end; + + default: + /* There is an argument if the previous + * character in user_path[] is '\0'. */ + if (i > 1 && user_path[i - 1] == '\0') + goto argument; + else + user_path[i] = tmp; + break; + } + + status = read(fd, &tmp, sizeof(char)); + if (status < 0) { + status = -errno; + goto end; + } + if ((size_t) status < sizeof(char)) { /* EOF */ + user_path[i] = '\0'; + argument[0] = '\0'; + status = 1; + goto end; + } + } + + /* The interpreter path is too long, truncate it. */ + user_path[i] = '\0'; + argument[0] = '\0'; + status = 1; + goto end; + +argument: + + /* Slurp the argument until the end-of-line. */ + for (i = 0; current_length < BINPRM_BUF_SIZE; current_length++, i++) { + switch (tmp) { + case '\n': + case '\r': + argument[i] = '\0'; + + /* Remove trailing spaces. */ + for (i--; i > 0 && (argument[i] == ' ' || argument[i] == '\t'); i--) + argument[i] = '\0'; + + status = 1; + goto end; + + default: + argument[i] = tmp; + break; + } + + status = read(fd, &tmp, sizeof(char)); + if (status < 0) { + status = -errno; + goto end; + } + if ((size_t) status < sizeof(char)) { /* EOF */ + argument[0] = '\0'; + status = 1; + goto end; + } + } + + /* The argument is too long, truncate it. */ + argument[i] = '\0'; + status = 1; + +end: + close(fd); + + /* Did an error occur or isn't a script? */ + if (status <= 0) + return status; + + return 1; +} + +/** + * Expand in argv[] the shebang of @user_path, if any. This function + * returns -errno if an error occurred, 1 if a shebang was found and + * extracted, otherwise 0. On success, both @host_path and @user_path + * point to the program to execute (respectively from host + * point-of-view and as-is), and @tracee's argv[] (pointed to by + * SYSARG_2) is correctly updated. + */ +int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]) +{ + ArrayOfXPointers *argv = NULL; + bool has_shebang = false; + + char argument[BINPRM_BUF_SIZE]; + int status; + size_t i; + + /* "The interpreter must be a valid pathname for an executable + * which is not itself a script [1]. If the filename + * argument of execve() specifies an interpreter script, then + * interpreter will be invoked with the following arguments: + * + * interpreter [optional-arg] filename arg... + * + * where arg... is the series of words pointed to by the argv + * argument of execve()." -- man 2 execve + * + * [1]: as of this writing (3.10.17) this is true only for the + * ELF interpreter; ie. a script can use a script as + * interpreter. + */ + for (i = 0; i < MAXSYMLINKS; i++) { + char *old_user_path; + + /* Translate this path (user -> host), then check it is executable. */ + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + /* Remember the initial user path. */ + old_user_path = talloc_strdup(tracee->ctx, user_path); + if (old_user_path == NULL) + return -ENOMEM; + + /* Extract into user_path and argument the shebang from host_path. */ + status = extract_shebang(tracee, host_path, user_path, argument); + if (status < 0) + return status; + + /* No more shebang. */ + if (status == 0) + break; + has_shebang = true; + + /* Translate new path (user -> host), then check it is executable. */ + status = translate_and_check_exec(tracee, host_path, user_path); + if (status < 0) + return status; + + /* Fetch argv[] only on demand. */ + if (argv == NULL) { + status = fetch_array_of_xpointers(tracee, &argv, SYSARG_2, 0); + if (status < 0) + return status; + } + + /* Assuming the shebang of "script" is "#!/bin/sh -x", + * a call to: + * + * execve("./script", { "script.sh", NULL }, ...) + * + * becomes: + * + * execve("/bin/sh", { "/bin/sh", "-x", "./script", NULL }, ...) + * + * See commit 8c8fbe85 about "argv->length == 1". */ + if (argument[0] != '\0') { + status = resize_array_of_xpointers(argv, 0, 2 + (argv->length == 1)); + if (status < 0) + return status; + + status = write_xpointees(argv, 0, 3, user_path, argument, old_user_path); + if (status < 0) + return status; + } + else { + status = resize_array_of_xpointers(argv, 0, 1 + (argv->length == 1)); + if (status < 0) + return status; + + status = write_xpointees(argv, 0, 2, user_path, old_user_path); + if (status < 0) + return status; + } + } + + if (i == MAXSYMLINKS) + return -ELOOP; + + /* Push argv[] only on demand. */ + if (argv != NULL) { + status = push_array_of_xpointers(argv, SYSARG_2); + if (status < 0) + return status; + } + + return (has_shebang ? 1 : 0); +} diff --git a/app/src/main/cpp/proot/src/execve/shebang.h b/app/src/main/cpp/proot/src/execve/shebang.h new file mode 100644 index 0000000..1ae63be --- /dev/null +++ b/app/src/main/cpp/proot/src/execve/shebang.h @@ -0,0 +1,32 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef SHEBANG_H +#define SHEBANG_H + +#include /* PATH_MAX, ARG_MAX, */ + +#include "tracee/tracee.h" + +extern int expand_shebang(Tracee *tracee, char host_path[PATH_MAX], char user_path[PATH_MAX]); + +#endif /* SHEBANG_H */ diff --git a/app/src/main/cpp/proot/src/loader/assembly-arm.h b/app/src/main/cpp/proot/src/loader/assembly-arm.h new file mode 100644 index 0000000..6848d4d --- /dev/null +++ b/app/src/main/cpp/proot/src/loader/assembly-arm.h @@ -0,0 +1,111 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/* According to the ARM EABI, all registers have undefined values at + * program startup except: + * + * - the instruction pointer (r15) + * - the stack pointer (r13) + * - the rtld_fini pointer (r0) + */ +#define BRANCH(stack_pointer, destination) do { \ + asm volatile ( \ + "// Restore initial stack pointer. \n\t" \ + "mov sp, %0 \n\t" \ + " \n\t" \ + "// Clear rtld_fini. \n\t" \ + "mov r0, #0 \n\t" \ + " \n\t" \ + "// Start the program. \n\t" \ + "bx %1 \n" \ + : /* no output */ \ + : "r" (stack_pointer), "r" (destination) \ + : "memory", "sp", "r0", "pc"); \ + __builtin_unreachable(); \ + } while (0) + +#define PREPARE_ARGS_1(arg1_) \ + register word_t arg1 asm("r0") = arg1_; \ + +#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + PREPARE_ARGS_1(arg1_) \ + register word_t arg2 asm("r1") = arg2_; \ + register word_t arg3 asm("r2") = arg3_; \ + +#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \ + PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + register word_t arg4 asm("r3") = arg4_; \ + register word_t arg5 asm("r4") = arg5_; \ + register word_t arg6 asm("r5") = arg6_; + +#define OUTPUT_CONTRAINTS_1 \ + "r" (arg1) + +#define OUTPUT_CONTRAINTS_3 \ + OUTPUT_CONTRAINTS_1, \ + "r" (arg2), "r" (arg3) + +#define OUTPUT_CONTRAINTS_6 \ + OUTPUT_CONTRAINTS_3, \ + "r" (arg4), "r" (arg5), "r" (arg6) + +#ifdef __thumb__ +#define STRINGIFY_EXPANDED(s) #s +#define SYSCALL(number_, nb_args, args...) \ + ({ \ + register word_t result asm("r0"); \ + PREPARE_ARGS_##nb_args(args) \ + asm volatile ( \ + "push {r7} \n\t" \ + "mov r7, #" STRINGIFY_EXPANDED(number_) "\n\t" \ + "svc #0x00000000 \n\t" \ + "pop {r7} \n\t" \ + : "=r" (result) \ + : OUTPUT_CONTRAINTS_##nb_args \ + : "memory"); \ + result; \ + }) +#else +#define SYSCALL(number_, nb_args, args...) \ + ({ \ + register word_t number asm("r7") = number_; \ + register word_t result asm("r0"); \ + PREPARE_ARGS_##nb_args(args) \ + asm volatile ( \ + "svc #0x00000000 \n\t" \ + : "=r" (result) \ + : "r" (number), \ + OUTPUT_CONTRAINTS_##nb_args \ + : "memory"); \ + result; \ + }) +#endif + +#define OPEN 5 +#define CLOSE 6 +#define MMAP 192 +#define MMAP_OFFSET_SHIFT 12 +#define EXECVE 11 +#define EXIT 1 +#define PRCTL 172 +#define MPROTECT 125 + diff --git a/app/src/main/cpp/proot/src/loader/assembly-arm64.h b/app/src/main/cpp/proot/src/loader/assembly-arm64.h new file mode 100644 index 0000000..4a79db3 --- /dev/null +++ b/app/src/main/cpp/proot/src/loader/assembly-arm64.h @@ -0,0 +1,98 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +/* According to the ARM64 EABI, all registers have undefined values at + * program startup except: + * + * - the instruction pointer (pc) + * - the stack pointer (sp) + * - the rtld_fini pointer (x0) + */ +#define BRANCH(stack_pointer, destination) do { \ + asm volatile ( \ + "// Restore initial stack pointer. \n\t" \ + "mov sp, %0 \n\t" \ + " \n\t" \ + "// Clear rtld_fini. \n\t" \ + "mov x0, #0 \n\t" \ + " \n\t" \ + "// Start the program. \n\t" \ + "br %1 \n" \ + : /* no output */ \ + : "r" (stack_pointer), "r" (destination) \ + : "memory", "sp", "x0"); \ + __builtin_unreachable(); \ + } while (0) + +#define PREPARE_ARGS_1(arg1_) \ + register word_t arg1 asm("x0") = arg1_; \ + +#define PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + PREPARE_ARGS_1(arg1_) \ + register word_t arg2 asm("x1") = arg2_; \ + register word_t arg3 asm("x2") = arg3_; \ + +#define PREPARE_ARGS_4(arg1_, arg2_, arg3_, arg4_) \ + PREPARE_ARGS_3(arg1_, arg2_, arg3_) \ + register word_t arg4 asm("x3") = arg4_; \ + +#define PREPARE_ARGS_6(arg1_, arg2_, arg3_, arg4_, arg5_, arg6_) \ + PREPARE_ARGS_4(arg1_, arg2_, arg3_, arg4_) \ + register word_t arg5 asm("x4") = arg5_; \ + register word_t arg6 asm("x5") = arg6_; + +#define OUTPUT_CONTRAINTS_1 \ + "r" (arg1) + +#define OUTPUT_CONTRAINTS_3 \ + OUTPUT_CONTRAINTS_1, \ + "r" (arg2), "r" (arg3) + +#define OUTPUT_CONTRAINTS_4 \ + OUTPUT_CONTRAINTS_3, \ + "r" (arg4) + +#define OUTPUT_CONTRAINTS_6 \ + OUTPUT_CONTRAINTS_4, \ + "r" (arg5), "r" (arg6) + +#define SYSCALL(number_, nb_args, args...) \ + ({ \ + register word_t number asm("x8") = number_; \ + register word_t result asm("x0"); \ + PREPARE_ARGS_##nb_args(args) \ + asm volatile ( \ + "svc #0x00000000 \n\t" \ + : "=r" (result) \ + : "r" (number), \ + OUTPUT_CONTRAINTS_##nb_args \ + : "memory"); \ + result; \ + }) + +#define OPENAT 56 +#define CLOSE 57 +#define MMAP 222 +#define EXECVE 221 +#define EXIT 93 +#define PRCTL 167 +#define MPROTECT 226 diff --git a/app/src/main/cpp/proot/src/loader/loader.c b/app/src/main/cpp/proot/src/loader/loader.c new file mode 100644 index 0000000..4842761 --- /dev/null +++ b/app/src/main/cpp/proot/src/loader/loader.c @@ -0,0 +1,259 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* bool, true, false, */ + +#define NO_LIBC_HEADER +#include "loader/script.h" +#include "compat.h" +#include "arch.h" + +#if defined(ARCH_ARM_EABI) + #include "loader/assembly-arm.h" +#elif defined(ARCH_ARM64) + #include "loader/assembly-arm64.h" +#else + #error "Unsupported architecture" +#endif + +#if !defined(MMAP_OFFSET_SHIFT) + #define MMAP_OFFSET_SHIFT 0 +#endif + +#define FATAL() do { \ + SYSCALL(EXIT, 1, 182); \ + __builtin_unreachable(); \ + } while (0) + +#define unlikely(expr) __builtin_expect(!!(expr), 0) + +/** + * Clear the memory from @start (inclusive) to @end (exclusive). + */ +static inline void clear(word_t start, word_t end) +{ + byte_t *start_misaligned; + byte_t *end_misaligned; + + word_t *start_aligned; + word_t *end_aligned; + + /* Compute the number of mis-aligned bytes. */ + word_t start_bytes = start % sizeof(word_t); + word_t end_bytes = end % sizeof(word_t); + + /* Compute aligned addresses. */ + start_aligned = (word_t *) (start_bytes ? start + sizeof(word_t) - start_bytes : start); + end_aligned = (word_t *) (end - end_bytes); + + /* Clear leading mis-aligned bytes. */ + start_misaligned = (byte_t *) start; + while (start_misaligned < (byte_t *) start_aligned) + *start_misaligned++ = 0; + + /* Clear aligned bytes. */ + while (start_aligned < end_aligned) + *start_aligned++ = 0; + + /* Clear trailing mis-aligned bytes. */ + end_misaligned = (byte_t *) end_aligned; + while (end_misaligned < (byte_t *) end) + *end_misaligned++ = 0; +} + +/** + * Return the address of the last path component of @string_. Note + * that @string_ is not modified. + */ +static inline word_t basename(word_t string_) +{ + byte_t *string = (byte_t *) string_; + byte_t *cursor; + + for (cursor = string; *cursor != 0; cursor++) + ; + + for (; *cursor != (byte_t) '/' && cursor > string; cursor--) + ; + + if (cursor != string) + cursor++; + + return (word_t) cursor; +} + +/** + * Interpret the load script pointed to by @cursor. + */ +void _start(void *cursor) +{ + bool traced = false; + bool reset_at_base = true; + word_t at_base = 0; + + word_t fd = -1; + word_t status; + + while(1) { + LoadStatement *stmt = cursor; + + switch (stmt->action) { + case LOAD_ACTION_OPEN_NEXT: + status = SYSCALL(CLOSE, 1, fd); + if (unlikely((int) status < 0)) + FATAL(); + /* Fall through. */ + + case LOAD_ACTION_OPEN: +#if defined(OPEN) + fd = SYSCALL(OPEN, 3, stmt->open.string_address, O_RDONLY, 0); +#else + fd = SYSCALL(OPENAT, 4, AT_FDCWD, stmt->open.string_address, O_RDONLY, 0); +#endif + if (unlikely((int) fd < 0)) + FATAL(); + + reset_at_base = true; + + cursor += LOAD_STATEMENT_SIZE(*stmt, open); + break; + + case LOAD_ACTION_MMAP_FILE: + status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length, + stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED, fd, + stmt->mmap.offset >> MMAP_OFFSET_SHIFT); + if (unlikely(status != stmt->mmap.addr)) + FATAL(); + + if (stmt->mmap.clear_length != 0) + clear(stmt->mmap.addr + stmt->mmap.length - stmt->mmap.clear_length, + stmt->mmap.addr + stmt->mmap.length); + + if (reset_at_base) { + at_base = stmt->mmap.addr; + reset_at_base = false; + } + + cursor += LOAD_STATEMENT_SIZE(*stmt, mmap); + break; + + case LOAD_ACTION_MMAP_ANON: + status = SYSCALL(MMAP, 6, stmt->mmap.addr, stmt->mmap.length, + stmt->mmap.prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); + if (unlikely(status != stmt->mmap.addr)) + FATAL(); + + cursor += LOAD_STATEMENT_SIZE(*stmt, mmap); + break; + + case LOAD_ACTION_MAKE_STACK_EXEC: + SYSCALL(MPROTECT, 3, + stmt->make_stack_exec.start, 1, + PROT_READ | PROT_WRITE | PROT_EXEC | PROT_GROWSDOWN); + + cursor += LOAD_STATEMENT_SIZE(*stmt, make_stack_exec); + break; + + case LOAD_ACTION_START_TRACED: + traced = true; + /* Fall through. */ + + case LOAD_ACTION_START: { + word_t *cursor2 = (word_t *) stmt->start.stack_pointer; + const word_t argc = cursor2[0]; + const word_t at_execfn = cursor2[1]; + word_t name; + + status = SYSCALL(CLOSE, 1, fd); + if (unlikely((int) status < 0)) + FATAL(); + + /* Right after execve, the stack content is as follow: + * + * +------+--------+--------+--------+ + * | argc | argv[] | envp[] | auxv[] | + * +------+--------+--------+--------+ + */ + + /* Skip argv[]. */ + cursor2 += argc + 1; + + /* Skip envp[]. */ + do cursor2++; while (cursor2[0] != 0); + cursor2++; + + /* Adjust auxv[]. */ + do { + switch (cursor2[0]) { + case AT_PHDR: + cursor2[1] = stmt->start.at_phdr; + break; + + case AT_PHENT: + cursor2[1] = stmt->start.at_phent; + break; + + case AT_PHNUM: + cursor2[1] = stmt->start.at_phnum; + break; + + case AT_ENTRY: + cursor2[1] = stmt->start.at_entry; + break; + + case AT_BASE: + cursor2[1] = at_base; + break; + + case AT_EXECFN: + /* stmt->start.at_execfn can't be used for now since it is + * currently stored in a location that will be scratched + * by the process (below the final stack pointer). */ + cursor2[1] = at_execfn; + break; + + default: + break; + } + cursor2 += 2; + } while (cursor2[0] != AT_NULL); + + /* Note that only 2 arguments are actually necessary... */ + name = basename(stmt->start.at_execfn); + SYSCALL(PRCTL, 3, PR_SET_NAME, name, 0); + + if (unlikely(traced)) + SYSCALL(EXECVE, 6, 1, + stmt->start.stack_pointer, + stmt->start.entry_point, 2, 3, 4); + else + BRANCH(stmt->start.stack_pointer, stmt->start.entry_point); + FATAL(); + } + + default: + FATAL(); + } + } + + FATAL(); +} diff --git a/app/src/main/cpp/proot/src/loader/script.h b/app/src/main/cpp/proot/src/loader/script.h new file mode 100644 index 0000000..6ae7621 --- /dev/null +++ b/app/src/main/cpp/proot/src/loader/script.h @@ -0,0 +1,78 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef SCRIPT +#define SCRIPT + +#include "arch.h" +#include "attribute.h" + +struct load_statement { + word_t action; + + union { + struct { + word_t string_address; + } open; + + struct { + word_t addr; + word_t length; + word_t prot; + word_t offset; + word_t clear_length; + } mmap; + + struct { + word_t start; + } make_stack_exec; + + struct { + word_t stack_pointer; + word_t entry_point; + word_t at_phdr; + word_t at_phent; + word_t at_phnum; + word_t at_entry; + word_t at_execfn; + } start; + }; +} PACKED; + +typedef struct load_statement LoadStatement; + +#define LOAD_STATEMENT_SIZE(statement, type) \ + (sizeof((statement).action) + sizeof((statement).type)) + +/* Don't use enum, since sizeof(enum) doesn't have to be equal to + * sizeof(word_t). Keep values in the same order as their respective + * actions appear in loader.c to get a change GCC produces a jump + * table. */ +#define LOAD_ACTION_OPEN_NEXT 0 +#define LOAD_ACTION_OPEN 1 +#define LOAD_ACTION_MMAP_FILE 2 +#define LOAD_ACTION_MMAP_ANON 3 +#define LOAD_ACTION_MAKE_STACK_EXEC 4 +#define LOAD_ACTION_START_TRACED 5 +#define LOAD_ACTION_START 6 + +#endif /* SCRIPT */ diff --git a/app/src/main/cpp/proot/src/path/binding.c b/app/src/main/cpp/proot/src/path/binding.c new file mode 100644 index 0000000..589e011 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/binding.c @@ -0,0 +1,728 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* lstat(2), */ +#include /* getcwd(2), lstat(2), */ +#include /* string(3), */ +#include /* bzero(3), */ +#include /* assert(3), */ +#include /* PATH_MAX, */ +#include /* E* */ +#include /* CIRCLEQ_*, */ +#include /* talloc_*, */ + +#include "path/binding.h" +#include "path/path.h" +#include "path/canon.h" +#include "cli/note.h" + +#include "compat.h" + +#define HEAD(tracee, side) \ + (side == GUEST \ + ? (tracee)->fs->bindings.guest \ + : (side == HOST \ + ? (tracee)->fs->bindings.host \ + : (tracee)->fs->bindings.pending)) + +#define NEXT(binding, side) \ + (side == GUEST \ + ? CIRCLEQ_NEXT(binding, link.guest) \ + : (side == HOST \ + ? CIRCLEQ_NEXT(binding, link.host) \ + : CIRCLEQ_NEXT(binding, link.pending))) + +#define CIRCLEQ_FOREACH_(tracee, binding, side) \ + for (binding = CIRCLEQ_FIRST(HEAD(tracee, side)); \ + binding != (void *) HEAD(tracee, side); \ + binding = NEXT(binding, side)) + +#define CIRCLEQ_INSERT_AFTER_(tracee, previous, binding, side) do { \ + switch (side) { \ + case GUEST: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.guest); break; \ + case HOST: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.host); break; \ + default: CIRCLEQ_INSERT_AFTER(HEAD(tracee, side), previous, binding, link.pending); break; \ + } \ + (void) talloc_reference(HEAD(tracee, side), binding); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE_(tracee, next, binding, side) do { \ + switch (side) { \ + case GUEST: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.guest); break; \ + case HOST: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.host); break; \ + default: CIRCLEQ_INSERT_BEFORE(HEAD(tracee, side), next, binding, link.pending); break; \ + } \ + (void) talloc_reference(HEAD(tracee, side), binding); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD_(tracee, binding, side) do { \ + switch (side) { \ + case GUEST: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.guest); break; \ + case HOST: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.host); break; \ + default: CIRCLEQ_INSERT_HEAD(HEAD(tracee, side), binding, link.pending); break; \ + } \ + (void) talloc_reference(HEAD(tracee, side), binding); \ +} while (0) + +#define IS_LINKED(binding, link) \ + ((binding)->link.cqe_next != NULL && (binding)->link.cqe_prev != NULL) + +#define CIRCLEQ_REMOVE_(tracee, binding, name) do { \ + CIRCLEQ_REMOVE((tracee)->fs->bindings.name, binding, link.name);\ + (binding)->link.name.cqe_next = NULL; \ + (binding)->link.name.cqe_prev = NULL; \ + talloc_unlink((tracee)->fs->bindings.name, binding); \ +} while (0) + + +/** + * Print all bindings (verbose purpose). + */ +static void print_bindings(const Tracee *tracee) +{ + const Binding *binding; + + if (tracee->fs->bindings.guest == NULL) + return; + + CIRCLEQ_FOREACH_(tracee, binding, GUEST) { + if (compare_paths(binding->host.path, binding->guest.path) == PATHS_ARE_EQUAL) + note(tracee, INFO, USER, "binding = %s", binding->host.path); + else + note(tracee, INFO, USER, "binding = %s:%s", + binding->host.path, binding->guest.path); + } +} + +/** + * Get the binding for the given @path (relatively to the given + * binding @side). + */ +Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]) +{ + Binding *binding; + size_t path_length = strlen(path); + + /* Sanity checks. */ + assert(path != NULL && path[0] == '/'); + + CIRCLEQ_FOREACH_(tracee, binding, side) { + Comparison comparison; + const Path *ref; + + switch (side) { + case GUEST: + ref = &binding->guest; + break; + + case HOST: + ref = &binding->host; + break; + + default: + assert(0); + return NULL; + } + + comparison = compare_paths2(ref->path, ref->length, path, path_length); + if ( comparison != PATHS_ARE_EQUAL + && comparison != PATH1_IS_PREFIX) + continue; + + /* Avoid false positive when a prefix of the rootfs is + * used as an asymmetric binding, ex.: + * + * proot -m /usr:/location /usr/local/slackware + */ + if ( side == HOST + && compare_paths(get_root(tracee), "/") != PATHS_ARE_EQUAL + && belongs_to_guestfs(tracee, path)) + continue; + + return binding; + } + + return NULL; +} + +/** + * Get the binding path for the given @path (relatively to the given + * binding @side). + */ +const char *get_path_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]) +{ + const Binding *binding; + + binding = get_binding(tracee, side, path); + if (!binding) + return NULL; + + switch (side) { + case GUEST: + return binding->guest.path; + + case HOST: + return binding->host.path; + + default: + assert(0); + return NULL; + } +} + +/** + * Return the path to the guest rootfs for the given @tracee, from the + * host point-of-view obviously. Depending on whether + * initialize_bindings() was called or not, the path is retrieved from + * the "bindings.guest" list or from the "bindings.pending" list, + * respectively. + */ +const char *get_root(const Tracee* tracee) +{ + const Binding *binding; + + if (tracee == NULL || tracee->fs == NULL) + return NULL; + + if (tracee->fs->bindings.guest == NULL) { + if (tracee->fs->bindings.pending == NULL + || CIRCLEQ_EMPTY(tracee->fs->bindings.pending)) + return NULL; + + binding = CIRCLEQ_LAST(tracee->fs->bindings.pending); + if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL) + return NULL; + + return binding->host.path; + } + + assert(!CIRCLEQ_EMPTY(tracee->fs->bindings.guest)); + + binding = CIRCLEQ_LAST(tracee->fs->bindings.guest); + + assert(strcmp(binding->guest.path, "/") == 0); + + return binding->host.path; +} + +/** + * Substitute the guest path (if any) with the host path in @path. + * This function returns: + * + * * -errno if an error occured + * + * * 0 if it is a binding location but no substitution is needed + * ("symetric" binding) + * + * * 1 if it is a binding location and a substitution was performed + * ("asymmetric" binding) + */ +int substitute_binding(const Tracee *tracee, Side side, char path[PATH_MAX]) +{ + const Path *reverse_ref; + const Path *ref; + const Binding *binding; + + binding = get_binding(tracee, side, path); + if (!binding) + return -ENOENT; + + /* Is it a "symetric" binding? */ + if (!binding->need_substitution) + return 0; + + switch (side) { + case GUEST: + ref = &binding->guest; + reverse_ref = &binding->host; + break; + + case HOST: + ref = &binding->host; + reverse_ref = &binding->guest; + break; + + default: + assert(0); + return -EACCES; + } + + substitute_path_prefix(path, ref->length, reverse_ref->path, reverse_ref->length); + + return 1; +} + +/** + * Remove @binding from all the @tracee's lists of bindings it belongs to. + */ +void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding) +{ + if (IS_LINKED(binding, link.pending)) + CIRCLEQ_REMOVE_(tracee, binding, pending); + + if (IS_LINKED(binding, link.guest)) + CIRCLEQ_REMOVE_(tracee, binding, guest); + + if (IS_LINKED(binding, link.host)) + CIRCLEQ_REMOVE_(tracee, binding, host); +} + +/** + * Insert @binding into the list of @bindings, in a sorted manner so + * as to make the substitution of nested bindings determistic, ex.: + * + * -b /bin:/foo/bin -b /usr/bin/more:/foo/bin/more + * + * Note: "nested" from the @side point-of-view. + */ +static void insort_binding(const Tracee *tracee, Side side, Binding *binding) +{ + Binding *iterator; + Binding *previous = NULL; + Binding *next = CIRCLEQ_FIRST(HEAD(tracee, side)); + + /* Find where it should be added in the list. */ + CIRCLEQ_FOREACH_(tracee, iterator, side) { + Comparison comparison; + const Path *binding_path; + const Path *iterator_path; + + switch (side) { + case PENDING: + case GUEST: + binding_path = &binding->guest; + iterator_path = &iterator->guest; + break; + + case HOST: + binding_path = &binding->host; + iterator_path = &iterator->host; + break; + + default: + assert(0); + return; + } + + comparison = compare_paths2(binding_path->path, binding_path->length, + iterator_path->path, iterator_path->length); + switch (comparison) { + case PATHS_ARE_EQUAL: + if (side == HOST) { + previous = iterator; + break; + } + + if (tracee->verbose > 0 && getenv("PROOT_IGNORE_MISSING_BINDINGS") == NULL) { + note(tracee, WARNING, USER, + "both '%s' and '%s' are bound to '%s', " + "only the last binding is active.", + iterator->host.path, binding->host.path, + binding->guest.path); + } + + /* Replace this iterator with the new binding. */ + CIRCLEQ_INSERT_AFTER_(tracee, iterator, binding, side); + remove_binding_from_all_lists(tracee, iterator); + return; + + case PATH1_IS_PREFIX: + /* The new binding contains the iterator. */ + previous = iterator; + break; + + case PATH2_IS_PREFIX: + /* The iterator contains the new binding. + * Use the deepest container. */ + if (next == (void *) HEAD(tracee, side)) + next = iterator; + break; + + case PATHS_ARE_NOT_COMPARABLE: + break; + + default: + assert(0); + return; + } + } + + /* Insert this binding in the list. */ + if (previous != NULL) + CIRCLEQ_INSERT_AFTER_(tracee, previous, binding, side); + else if (next != (void *) HEAD(tracee, side)) + CIRCLEQ_INSERT_BEFORE_(tracee, next, binding, side); + else + CIRCLEQ_INSERT_HEAD_(tracee, binding, side); +} + +/** + * c.f. function above. + */ +static void insort_binding2(const Tracee *tracee, Binding *binding) +{ + binding->need_substitution = + compare_paths(binding->host.path, binding->guest.path) != PATHS_ARE_EQUAL; + + insort_binding(tracee, GUEST, binding); + insort_binding(tracee, HOST, binding); +} + +/** + * Create and insert a new binding (@host_path:@guest_path) into the + * list of @tracee's bindings. The Talloc parent of this new binding + * is @context. This function returns NULL if an error occurred, + * otherwise a pointer to the newly created binding. + */ +Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context, + const char host_path[PATH_MAX], + const char guest_path[PATH_MAX]) +{ + Binding *binding; + + binding = talloc_zero(context, Binding); + if (binding == NULL) + return NULL; + + strcpy(binding->host.path, host_path); + strcpy(binding->guest.path, guest_path); + + binding->host.length = strlen(binding->host.path); + binding->guest.length = strlen(binding->guest.path); + + insort_binding2(tracee, binding); + + return binding; +} + +/** + * Free all bindings from @bindings. + * + * Note: this is a Talloc destructor. + */ +static int remove_bindings(Bindings *bindings) +{ + Binding *binding; + Tracee *tracee; + + /* Unlink all bindings from the @link list. */ +#define CIRCLEQ_REMOVE_ALL(name) do { \ + binding = CIRCLEQ_FIRST(bindings); \ + while (binding != (void *) bindings) { \ + Binding *next = CIRCLEQ_NEXT(binding, link.name);\ + CIRCLEQ_REMOVE_(tracee, binding, name); \ + binding = next; \ + } \ +} while (0) + + /* Search which link is used by this list. */ + tracee = TRACEE(bindings); + if (bindings == tracee->fs->bindings.pending) + CIRCLEQ_REMOVE_ALL(pending); + else if (bindings == tracee->fs->bindings.guest) + CIRCLEQ_REMOVE_ALL(guest); + else if (bindings == tracee->fs->bindings.host) + CIRCLEQ_REMOVE_ALL(host); + + bzero(bindings, sizeof(Bindings)); + + return 0; +} + +/** + * Allocate a new binding "@host:@guest" and attach it to + * @tracee->fs->bindings.pending. This function complains about + * missing @host path only if @must_exist is true. This function + * returns the allocated binding on success, NULL on error. + */ +Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist) +{ + Binding *binding; + char base[PATH_MAX]; + int status; + + /* Lasy allocation of the list of bindings specified by the + * user. This list will be used by initialize_bindings(). */ + if (tracee->fs->bindings.pending == NULL) { + tracee->fs->bindings.pending = talloc_zero(tracee->fs, Bindings); + if (tracee->fs->bindings.pending == NULL) + return NULL; + CIRCLEQ_INIT(tracee->fs->bindings.pending); + talloc_set_destructor(tracee->fs->bindings.pending, remove_bindings); + } + + /* Allocate an empty binding. */ + binding = talloc_zero(tracee->ctx, Binding); + if (binding == NULL) + return NULL; + + /* Canonicalize the host part of the binding, as expected by + * get_binding(). */ + status = realpath2(tracee->reconf.tracee, binding->host.path, host, true); + if (status < 0) { + if (must_exist && getenv("PROOT_IGNORE_MISSING_BINDINGS") == NULL) + note(tracee, WARNING, INTERNAL, "can't sanitize binding \"%s\": %s", + host, strerror(-status)); + goto error; + } + binding->host.length = strlen(binding->host.path); + + /* Symetric binding? */ + guest = guest ?: host; + + /* When not absolute, assume the guest path is relative to the + * current working directory, as with ``-b .`` for instance. */ + if (guest[0] != '/') { + status = getcwd2(tracee->reconf.tracee, base); + if (status < 0) { + note(tracee, WARNING, INTERNAL, "can't sanitize binding \"%s\": %s", + binding->guest.path, strerror(-status)); + goto error; + } + } + else + strcpy(base, "/"); + + join_paths(binding->guest.path, base, guest); + binding->guest.length = strlen(binding->guest.path); + + /* Keep the list of bindings specified by the user ordered, + * for the sake of consistency. For instance binding to "/" + * has to be the last in the list. */ + insort_binding(tracee, PENDING, binding); + + return binding; + +error: + TALLOC_FREE(binding); + return NULL; +} + +/** + * Canonicalize the guest part of the given @binding, insert it into + * @tracee->fs->bindings.guest and @tracee->fs->bindings.host. This + * function returns -1 if an error occured, 0 otherwise. + */ +static void initialize_binding(Tracee *tracee, Binding *binding) +{ + char path[PATH_MAX]; + struct stat statl; + int status; + + /* All bindings but "/" must be canonicalized. The exception + * for "/" is required to bootstrap the canonicalization. */ + if (compare_paths(binding->guest.path, "/") != PATHS_ARE_EQUAL) { + bool dereference; + size_t length; + + strcpy(path, binding->guest.path); + length = strlen(path); + assert(length > 0); + + /* Does the user explicitly tell not to dereference + * guest path? */ + dereference = (path[length - 1] != '!'); + if (!dereference) + path[length - 1] = '\0'; + + /* Initial state before canonicalization. */ + strcpy(binding->guest.path, "/"); + + /* Remember the type of the final component, it will + * be used in build_glue() later. */ + status = lstat(binding->host.path, &statl); + tracee->glue_type = (status < 0 || S_ISBLK(statl.st_mode) || S_ISCHR(statl.st_mode) + ? S_IFREG : statl.st_mode & S_IFMT); + + /* Sanitize the guest path of the binding within the + alternate rootfs since it is assumed by + substitute_binding(). */ + status = canonicalize(tracee, path, dereference, binding->guest.path, 0); + if (status < 0) { + note(tracee, WARNING, INTERNAL, + "sanitizing the guest path (binding) \"%s\": %s", + path, strerror(-status)); + return; + } + + /* Remove the trailing "/" or "/." as expected by + * substitute_binding(). */ + chop_finality(binding->guest.path); + + /* Disable definitively the creation of the glue for + * this binding. */ + tracee->glue_type = 0; + } + + binding->guest.length = strlen(binding->guest.path); + + insort_binding2(tracee, binding); +} + +/** + * Add bindings induced by @new_binding when @tracee is being sub-reconfigured. + * For example, if the previous configuration ("-r /rootfs1") contains this + * binding: + * + * -b /home/ced:/usr/local/ced + * + * and if the current configuration ("-r /rootfs2") introduces such a new + * binding: + * + * -b /usr:/media + * + * then the following binding is induced: + * + * -b /home/ced:/media/local/ced + */ +static void add_induced_bindings(Tracee *tracee, const Binding *new_binding) +{ + Binding *old_binding; + char path[PATH_MAX]; + int status; + + /* Only for reconfiguration. */ + if (tracee->reconf.tracee == NULL) + return; + + /* From the example, PRoot has already converted "-b /usr:/media" into + * "-b /rootfs1/usr:/media" in order to ensure the host part is really a + * host path. Here, the host part is converted back to "/usr" since the + * comparison can't be made on "/rootfs1/usr". + */ + strcpy(path, new_binding->host.path); + status = detranslate_path(tracee->reconf.tracee, path, NULL); + if (status < 0) + return; + + CIRCLEQ_FOREACH_(tracee->reconf.tracee, old_binding, GUEST) { + Binding *induced_binding; + Comparison comparison; + char path2[PATH_MAX]; + size_t prefix_length; + + /* Check if there's an induced binding by searching a common + * path prefix in between new/old bindings: + * + * -b /home/ced:[/usr]/local/ced + * -b [/usr]:/media + */ + comparison = compare_paths(path, old_binding->guest.path); + if (comparison != PATH1_IS_PREFIX) + continue; + + /* Convert the path of this induced binding to the new + * filesystem namespace. From the example, "/usr/local/ced" is + * converted into "/media/local/ced". Note: substitute_binding + * can't be used in this case since it would expect + * "/rootfs1/usr/local/ced instead". + */ + prefix_length = strlen(path); + if (prefix_length == 1) + prefix_length = 0; + + join_paths(path2, new_binding->guest.path, old_binding->guest.path + prefix_length); + + /* Install the induced binding. From the example: + * + * -b /home/ced:/media/local/ced + */ + induced_binding = talloc_zero(tracee->ctx, Binding); + if (induced_binding == NULL) + continue; + + strcpy(induced_binding->host.path, old_binding->host.path); + strcpy(induced_binding->guest.path, path2); + + induced_binding->host.length = strlen(induced_binding->host.path); + induced_binding->guest.length = strlen(induced_binding->guest.path); + + VERBOSE(tracee, 2, "induced binding: %s:%s (old) & %s:%s (new) -> %s:%s (induced)", + old_binding->host.path, old_binding->guest.path, path, new_binding->guest.path, + induced_binding->host.path, induced_binding->guest.path); + + insort_binding2(tracee, induced_binding); + } +} + +/** + * Allocate @tracee->fs->bindings.guest and + * @tracee->fs->bindings.host, then call initialize_binding() on each + * binding listed in @tracee->fs->bindings.pending. + */ +int initialize_bindings(Tracee *tracee) +{ + Binding *binding; + + /* Sanity checks. */ + assert(get_root(tracee) != NULL); + assert(tracee->fs->bindings.pending != NULL); + assert(tracee->fs->bindings.guest == NULL); + assert(tracee->fs->bindings.host == NULL); + + /* Allocate @tracee->fs->bindings.guest and + * @tracee->fs->bindings.host. */ + tracee->fs->bindings.guest = talloc_zero(tracee->fs, Bindings); + tracee->fs->bindings.host = talloc_zero(tracee->fs, Bindings); + if (tracee->fs->bindings.guest == NULL || tracee->fs->bindings.host == NULL) { + note(tracee, ERROR, INTERNAL, "can't allocate enough memory"); + TALLOC_FREE(tracee->fs->bindings.guest); + TALLOC_FREE(tracee->fs->bindings.host); + return -1; + } + + CIRCLEQ_INIT(tracee->fs->bindings.guest); + CIRCLEQ_INIT(tracee->fs->bindings.host); + + talloc_set_destructor(tracee->fs->bindings.guest, remove_bindings); + talloc_set_destructor(tracee->fs->bindings.host, remove_bindings); + + /* The binding to "/" has to be installed before other + * bindings since this former is required to canonicalize + * these latters. */ + binding = CIRCLEQ_LAST(tracee->fs->bindings.pending); + assert(compare_paths(binding->guest.path, "/") == PATHS_ARE_EQUAL); + + /* Call initialize_binding() on each pending binding in + * reverse order: the last binding "/" is used to bootstrap + * the canonicalization. */ + while (binding != (void *) tracee->fs->bindings.pending) { + Binding *previous; + previous = CIRCLEQ_PREV(binding, link.pending); + + /* Canonicalize then insert this binding into + * tracee->fs->bindings.guest/host. */ + initialize_binding(tracee, binding); + + /* Add induced bindings on sub-reconfiguration. */ + add_induced_bindings(tracee, binding); + + binding = previous; + } + + TALLOC_FREE(tracee->fs->bindings.pending); + + if (tracee->verbose > 0) + print_bindings(tracee); + + return 0; +} diff --git a/app/src/main/cpp/proot/src/path/binding.h b/app/src/main/cpp/proot/src/path/binding.h new file mode 100644 index 0000000..441a902 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/binding.h @@ -0,0 +1,57 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef BINDING_H +#define BINDING_H + +#include /* PATH_MAX, */ +#include + +#include "tracee/tracee.h" +#include "path.h" + +typedef struct binding { + Path host; + Path guest; + + bool need_substitution; + + struct { + CIRCLEQ_ENTRY(binding) pending; + CIRCLEQ_ENTRY(binding) guest; + CIRCLEQ_ENTRY(binding) host; + } link; +} Binding; + +typedef CIRCLEQ_HEAD(bindings, binding) Bindings; + +extern Binding *insort_binding3(const Tracee *tracee, const TALLOC_CTX *context, + const char host_path[PATH_MAX], const char guest_path[PATH_MAX]); +extern Binding *new_binding(Tracee *tracee, const char *host, const char *guest, bool must_exist); +extern int initialize_bindings(Tracee *tracee); +extern const char *get_path_binding(const Tracee* tracee, Side side, const char path[PATH_MAX]); +extern Binding *get_binding(const Tracee *tracee, Side side, const char path[PATH_MAX]); +extern const char *get_root(const Tracee* tracee); +extern int substitute_binding(const Tracee* tracee, Side side, char path[PATH_MAX]); +extern void remove_binding_from_all_lists(const Tracee *tracee, Binding *binding); + +#endif /* BINDING_H */ diff --git a/app/src/main/cpp/proot/src/path/canon.c b/app/src/main/cpp/proot/src/path/canon.c new file mode 100644 index 0000000..fb939bd --- /dev/null +++ b/app/src/main/cpp/proot/src/path/canon.c @@ -0,0 +1,340 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* pid_t */ +#include /* PATH_MAX, */ +#include /* MAXSYMLINKS, */ +#include /* E*, */ +#include /* lstat(2), S_ISREG(), */ +#include /* access(2), lstat(2), */ +#include /* string(3), */ +#include /* assert(3), */ +#include /* sscanf(3), */ + +#include "path/canon.h" +#include "path/path.h" +#include "path/binding.h" +#include "path/glue.h" +#include "path/proc.h" + +/** + * Put an end-of-string ('\0') right before the last component of @path. + */ +static inline void pop_component(char *path) +{ + int offset; + + offset = strlen(path) - 1; + assert(offset >= 0); + + /* Don't pop over "/", it doesn't mean anything. */ + if (offset == 0) { + assert(path[0] == '/' && path[1] == '\0'); + return; + } + + /* Skip trailing path separators. */ + while (offset > 1 && path[offset] == '/') + offset--; + + /* Search for the previous path separator. */ + while (offset > 1 && path[offset] != '/') + offset--; + + /* Cut the end of the string before the last component. */ + path[offset] = '\0'; + assert(path[0] == '/'); +} + +/** + * Copy in @component the first path component pointed to by @cursor, + * this later is updated to point to the next component for a further + * call. This function returns: + * + * - -errno if an error occured. + * + * - FINAL_SLASH if it the last component of the path but we + * really expect a directory. + * + * - FINAL_NORMAL if it the last component of the path. + * + * - 0 otherwise. + */ +static inline Finality next_component(char component[NAME_MAX], const char **cursor) +{ + const char *start; + ptrdiff_t length; + bool want_dir; + + /* Skip leading path separators. */ + while (**cursor != '\0' && **cursor == '/') + (*cursor)++; + + /* Find the next component. */ + start = *cursor; + while (**cursor != '\0' && **cursor != '/') + (*cursor)++; + length = *cursor - start; + + if (length >= NAME_MAX) + return -ENAMETOOLONG; + + /* Extract the component. */ + strncpy(component, start, length); + component[length] = '\0'; + + /* Check if a [link to a] directory is expected. */ + want_dir = (**cursor == '/'); + + /* Skip trailing path separators. */ + while (**cursor != '\0' && **cursor == '/') + (*cursor)++; + + if (**cursor == '\0') + return (want_dir + ? FINAL_SLASH + : FINAL_NORMAL); + + return NOT_FINAL; +} + +/** + * Resolve bindings (if any) in @guest_path and copy the translated + * path into @host_path. Also, this function checks that a non-final + * component is either a directory (returned value is 0) or a symlink + * (returned value is 1), otherwise it returns -errno (-ENOENT or + * -ENOTDIR). + */ +static inline int substitute_binding_stat(Tracee *tracee, Finality finality, unsigned int recursion_level, + const char guest_path[PATH_MAX], char host_path[PATH_MAX]) +{ + struct stat statl; + int status; + + strcpy(host_path, guest_path); + status = substitute_binding(tracee, GUEST, host_path); + if (status < 0) + return status; + + statl.st_mode = 0; + status = lstat(host_path, &statl); + + /* Build the glue between the hostfs and the guestfs during + * the initialization of a binding. */ + if (status < 0 && tracee->glue_type != 0) { + statl.st_mode = build_glue(tracee, guest_path, host_path, finality); + if (statl.st_mode == 0) + status = -1; + } + + /* Return an error if a non-final component isn't a directory + * nor a symlink. The error depends on why the component + * could not be accessed (ENOENT, EACCES, ...), otherwise the + * error is "Not a directory". */ + if (!IS_FINAL(finality) && !S_ISDIR(statl.st_mode) && !S_ISLNK(statl.st_mode)) + return (status < 0 ? -ENOENT : -ENOTDIR); + + return (S_ISLNK(statl.st_mode) ? 1 : 0); +} + +/** + * Copy in @guest_path the canonicalization (see `man 3 realpath`) of + * @user_path regarding to @tracee->root. The path to canonicalize + * could be either absolute or relative to @guest_path. When the last + * component of @user_path is a link, it is dereferenced only if + * @deref_final is true -- it is useful for syscalls like lstat(2). + * The parameter @recursion_level should be set to 0 unless you know + * what you are doing. This function returns -errno if an error + * occured, otherwise it returns 0. + */ +int canonicalize(Tracee *tracee, const char *user_path, bool deref_final, + char guest_path[PATH_MAX], unsigned int recursion_level) +{ + char scratch_path[PATH_MAX]; + char host_path[PATH_MAX]; + Finality finality; + const char *cursor; + int status; + + /* Avoid infinite loop on circular links. */ + if (recursion_level > MAXSYMLINKS) + return -ELOOP; + + if (user_path[0] != '/') { + /* Ensure 'guest_path' contains an absolute base of + * the relative `user_path`. */ + if (guest_path[0] != '/') + return -EINVAL; + } + else + strcpy(guest_path, "/"); + + /* Resolve bindings for the initial '/' component or user_path, + * which is not handled in the loop below. + * In particular HOST_PATH extensions are called from there. */ + status = substitute_binding_stat(tracee, NOT_FINAL, recursion_level, guest_path, host_path); + if (status < 0) + return status; + + /* Canonicalize recursely 'user_path' into 'guest_path'. */ + cursor = user_path; + finality = NOT_FINAL; + while (!IS_FINAL(finality)) { + Comparison comparison; + char component[NAME_MAX]; + + finality = next_component(component, &cursor); + status = (int) finality; + if (status < 0) + return status; + + if (strcmp(component, ".") == 0) { + if (IS_FINAL(finality)) + finality = FINAL_DOT; + continue; + } + + if (strcmp(component, "..") == 0) { + pop_component(guest_path); + if (IS_FINAL(finality)) + finality = FINAL_SLASH; + continue; + } + + join_paths(scratch_path, guest_path, component); + + /* Resolve bindings and check that a non-final + * component exists and either is a directory or is a + * symlink. For this latter case, we check that the + * symlink points to a directory once it is + * canonicalized, at the end of this loop. */ + status = substitute_binding_stat(tracee, finality, recursion_level, scratch_path, host_path); + if (status < 0) + return status; + + /* Nothing special to do if it's not a link or if we + * explicitly ask to not dereference 'user_path', as + * required by syscalls like lstat(2). Obviously, this + * later condition does not apply to intermediate path + * components. Errors are explicitly ignored since + * they should be handled by the caller. */ + if (status <= 0 || (finality == FINAL_NORMAL && !deref_final)) { + strcpy(scratch_path, guest_path); + join_paths(guest_path, scratch_path, component); + continue; + } + + /* It's a link, so we have to dereference *and* + * canonicalize to ensure we are not going outside the + * new root. */ + comparison = compare_paths("/proc", guest_path); + switch (comparison) { + case PATHS_ARE_EQUAL: + case PATH1_IS_PREFIX: + /* Some links in "/proc" are generated + * dynamically by the kernel. PRoot has to + * emulate some of them. */ + status = readlink_proc(tracee, scratch_path, + guest_path, component, comparison); + switch (status) { + case CANONICALIZE: + /* The symlink is already dereferenced, + * now canonicalize it. */ + goto canon; + + case DONT_CANONICALIZE: + /* If and only very final, this symlink + * shouldn't be dereferenced nor canonicalized. */ + if (finality == FINAL_NORMAL) { + strcpy(guest_path, scratch_path); + return 0; + } + break; + + default: + if (status < 0) + return status; + } + + default: + break; + } + + status = readlink(host_path, scratch_path, sizeof(scratch_path)); + if (status < 0) + return status; + else if (status == sizeof(scratch_path)) + return -ENAMETOOLONG; + scratch_path[status] = '\0'; + + /* Remove the leading "root" part if needed, it's + * useful for "/proc/self/cwd/" for instance. */ + status = detranslate_path(tracee, scratch_path, host_path); + if (status < 0) + return status; + + canon: + /* Canonicalize recursively the referee in case it + * is/contains a link, moreover if it is not an + * absolute link then it is relative to + * 'guest_path'. */ + status = canonicalize(tracee, scratch_path, true, guest_path, recursion_level + 1); + if (status < 0) + return status; + + /* Check that a non-final canonicalized/dereferenced + * symlink exists and is a directory. */ + status = substitute_binding_stat(tracee, finality, recursion_level, guest_path, host_path); + if (status < 0) + return status; + + /* Here, 'guest_path' shouldn't be a symlink anymore, + * unless it is a named file descriptor. */ + assert(status != 1 || sscanf(guest_path, "/proc/%*d/fd/%d", &status) == 1); + } + + /* At the exit stage of the first level of recursion, + * `guest_path` is fully canonicalized but a terminating '/' + * or a terminating '.' may be required to keep the initial + * semantic of `user_path`. */ + if (recursion_level == 0) { + switch (finality) { + case FINAL_NORMAL: + break; + + case FINAL_SLASH: + strcpy(scratch_path, guest_path); + join_paths(guest_path, scratch_path, ""); + break; + + case FINAL_DOT: + strcpy(scratch_path, guest_path); + join_paths(guest_path, scratch_path, "."); + break; + + default: + assert(0); + } + } + + return 0; +} diff --git a/app/src/main/cpp/proot/src/path/canon.h b/app/src/main/cpp/proot/src/path/canon.h new file mode 100644 index 0000000..fa6546f --- /dev/null +++ b/app/src/main/cpp/proot/src/path/canon.h @@ -0,0 +1,34 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef CANON_H +#define CANON_H + +#include +#include + +#include "tracee/tracee.h" + +extern int canonicalize(Tracee *tracee, const char *user_path, bool deref_final, + char guest_path[PATH_MAX], unsigned int nb_recursion); + +#endif /* CANON_H */ diff --git a/app/src/main/cpp/proot/src/path/glue.c b/app/src/main/cpp/proot/src/path/glue.c new file mode 100644 index 0000000..14975a4 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/glue.c @@ -0,0 +1,189 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* mkdir(2), lstat(2), */ +#include /* mkdir(2), lstat(2), */ +#include /* mknod(2), */ +#include /* mknod(2), lstat(2), unlink(2), rmdir(2), */ +#include /* string(3), */ +#include /* assert(3), */ +#include /* PATH_MAX, */ +#include /* errno, E* */ +#include /* talloc_*, */ + +#include "path/binding.h" +#include "path/path.h" +#include "path/temp.h" +#include "cli/note.h" + +#include "compat.h" + +/** + * Remove @path if it is empty only. + * + * Note: this is a Talloc destructor. + */ +static int remove_placeholder(char *path) +{ + struct stat statl; + int status; + + status = lstat(path, &statl); + if (status) + return 0; /* Not fatal. */ + + if (!S_ISDIR(statl.st_mode)) { + if (statl.st_size != 0) + return 0; /* Not fatal. */ + status = unlink(path); + } + else + status = rmdir(path); + if (status) + return 0; /* Not fatal. */ + + return 0; +} + +/** + * Attach a copy of @path to the autofree context, and set its + * destructor to remove_placeholder(). + */ +static void set_placeholder_destructor(const char *path) +{ + TALLOC_CTX *autofreed; + char *placeholder; + + autofreed = talloc_autofree_context(); + if (autofreed == NULL) + return; + + placeholder = talloc_strdup(autofreed, path); + if (placeholder == NULL) + return; + + talloc_set_destructor(placeholder, remove_placeholder); +} + +/** + * Build in a temporary filesystem the glue between the guest part and + * the host part of the @binding_path. This function returns the type + * of the bound path, otherwise 0 if an error occured. + * + * For example, assuming the host path "/opt" is mounted/bound to the + * guest path "/black/holes/and/revelations", and assuming this path + * can't be created in the guest rootfs (eg. permission denied), then + * it is created in a temporary rootfs and all these paths are glued + * that way: + * + * $GUEST/black/ --> $GLUE/black/ + * ./holes + * ./holes/and + * ./holes/and/revelations --> $HOST/opt/ + * + * This glue allows operations on paths that do not exist in the guest + * rootfs but that were specified as the guest part of a binding. + */ +mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX], + Finality finality) +{ + bool belongs_to_gluefs; + Comparison comparison; + Binding *binding; + mode_t type; + mode_t mode; + int status; + + assert(tracee->glue_type != 0); + + /* Create the temporary directory where the "glue" rootfs will + * lie. */ + if (tracee->glue == NULL) { + tracee->glue = create_temp_directory(NULL, global_tool_name); + if (tracee->glue == NULL) { + note(tracee, ERROR, INTERNAL, "can't create glue rootfs"); + return 0; + } + talloc_set_name_const(tracee->glue, "$glue"); + } + + comparison = compare_paths(tracee->glue, host_path); + belongs_to_gluefs = (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX); + + /* If it's not a final component then it is a directory. I definitely + * hate how the potential type of the final component is propagated + * from initialize_binding() down to here, sadly there's no elegant way + * to know its type at this stage. */ + if (IS_FINAL(finality)) { + type = tracee->glue_type; + mode = (belongs_to_gluefs ? 0777 : 0); + } + else { + type = S_IFDIR; + mode = 0777; + } + + if (getenv("PROOT_DONT_POLLUTE_ROOTFS") != NULL && !belongs_to_gluefs) + goto create_binding; + + /* Try to create this component into the "guest" or "glue" + * rootfs (depending if there were a glue previously). */ + if (S_ISDIR(type)) + status = mkdir(host_path, mode); + else /* S_IFREG, S_IFCHR, S_IFBLK, S_IFIFO or S_IFSOCK. */ + status = mknod(host_path, mode | type, 0); + + /* Remove placeholders from the guest rootfs once PRoot is + * terminated. */ + if (status >= 0 && !belongs_to_gluefs) + set_placeholder_destructor(host_path); + + /* Nothing else to do if the path already exists or if it is + * the final component since it will be pointed to by the + * binding being initialized (from the example, + * "$GUEST/black/holes/and/revelations" -> "$HOST/opt"). */ + if (status >= 0 || errno == EEXIST || IS_FINAL(finality)) + return type; + + /* mkdir/mknod are supposed to always succeed in + * tracee->glue. */ + if (belongs_to_gluefs) { + note(tracee, WARNING, SYSTEM, "mkdir/mknod"); + return 0; + } + +create_binding: + /* Sanity checks. */ + if ( strnlen(tracee->glue, PATH_MAX) >= PATH_MAX + || strnlen(guest_path, PATH_MAX) >= PATH_MAX) { + note(tracee, WARNING, INTERNAL, "installing the binding: guest path too long"); + return 0; + } + + /* From the example, create the binding "/black" -> + * "$GLUE/black". */ + binding = insort_binding3(tracee, tracee->glue, tracee->glue, guest_path); + if (binding == NULL) + return 0; + + return type; +} diff --git a/app/src/main/cpp/proot/src/path/glue.h b/app/src/main/cpp/proot/src/path/glue.h new file mode 100644 index 0000000..cebf3a9 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/glue.h @@ -0,0 +1,34 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef GLUE_H +#define GLUE_H + +#include /* PATH_MAX, */ + +#include "tracee/tracee.h" +#include "path.h" + +extern mode_t build_glue(Tracee *tracee, const char *guest_path, char host_path[PATH_MAX], + Finality finality); + +#endif /* GLUE_H */ diff --git a/app/src/main/cpp/proot/src/path/path.c b/app/src/main/cpp/proot/src/path/path.c new file mode 100644 index 0000000..0e2a8c3 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/path.c @@ -0,0 +1,661 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* string(3), */ +#include /* va_*(3), */ +#include /* assert(3), */ +#include /* AT_*, */ +#include /* readlink*(2), *stat(2), getpid(2), */ +#include /* pid_t, */ +#include /* S_ISDIR, */ +#include /* opendir(3), readdir(3), */ +#include /* snprintf(3), */ +#include /* E*, */ +#include /* ptrdiff_t, */ +#include /* PRI*, */ + +#include "path/path.h" +#include "path/binding.h" +#include "path/canon.h" +#include "path/proc.h" +#include "cli/note.h" + +#include "compat.h" + +/** + * Copy in @result the concatenation of several paths (@number_paths) + * and adds a path separator ('/') in between when needed. This + * function returns -errno if an error occured, otherwise it returns 0. + */ +void join_paths(char result[PATH_MAX], const char *path1, const char *path2) +{ + size_t length; + + strcpy(result, path1); + length = strlen(result); + + /* A new path separator is needed. */ + if (length > 0 && result[length - 1] != '/' && path2[0] != '/') { + strcat(result + length, "/"); + strcat(result + length, path2); + } + /* There are already two path separators. */ + else if (length > 0 && result[length - 1] == '/' && path2[0] == '/') { + strcat(result + length, path2 + 1); + } + /* There's already one path separator or result[] is empty. */ + else + strcat(result + length, path2); +} + +/** + * Put in @host_path the full path to the given shell @command. The + * @command is searched in @paths if not null, otherwise in $PATH + * (relatively to the @tracee's file-system name-space). This + * function always returns -1 on error, otherwise 0. + */ +int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], const char *command) +{ + char path[PATH_MAX]; + const char *cursor; + struct stat statr; + int status; + + bool is_explicit; + bool found; + + assert(command != NULL); + is_explicit = (strchr(command, '/') != NULL); + + /* Is the command available without any $PATH look-up? */ + status = realpath2(tracee, host_path, command, true); + if (status == 0 && stat(host_path, &statr) == 0) { + if (is_explicit && !S_ISREG(statr.st_mode)) { + note(tracee, ERROR, USER, "'%s' is not a regular file", command); + return -EACCES; + } + + if (is_explicit && (statr.st_mode & S_IXUSR) == 0) { + note(tracee, ERROR, USER, "'%s' is not executable", command); + return -EACCES; + } + + found = true; + + /* Don't dereference the final component to preserve + * argv0 in case it is a symlink to script. */ + (void) realpath2(tracee, host_path, command, false); + } + else + found = false; + + /* Is the the explicit command was found? */ + if (is_explicit) { + if (found) + return 0; + else + goto not_found; + } + + /* Otherwise search the command in $PATH. */ + paths = paths ?: getenv("PATH"); + if (paths == NULL || strcmp(paths, "") == 0) + goto not_found; + + cursor = paths; + do { + size_t length; + + length = strcspn(cursor, ":"); + cursor += length + 1; + + if (length >= PATH_MAX) + continue; + else if (length == 0) + strcpy(path, "."); + else { + strncpy(path, cursor - length - 1, length); + path[length] = '\0'; + } + + /* Avoid buffer-overflow. */ + if (length + strlen(command) + 2 >= PATH_MAX) + continue; + + strcat(path, "/"); + strcat(path, command); + + status = realpath2(tracee, host_path, path, true); + if (status == 0 + && stat(host_path, &statr) == 0 + && S_ISREG(statr.st_mode) + && (statr.st_mode & S_IXUSR) != 0) { + /* Don't dereference the final component to preserve + * argv0 in case it is a symlink to script. */ + (void) realpath2(tracee, host_path, path, false); + return 0; + } + } while (*(cursor - 1) != '\0'); + +not_found: + status = getcwd2(tracee, path); + if (status < 0) + strcpy(path, ""); + + note(tracee, ERROR, USER, "'%s' not found (root = %s, cwd = %s, $PATH=%s)", + command, get_root(tracee), path, paths); + + /* Check if the command was found without any $PATH look-up + * but it didn't contain "/". */ + if (found && !is_explicit) + note(tracee, ERROR, USER, + "to execute a local program, use the './' prefix, for example: ./%s", command); + + return -1; +} + +/** + * Put in @host_path the canonicalized form of @path. In the nominal + * case (@tracee == NULL), this function is barely equivalent to + * realpath(), but when doing sub-reconfiguration, the path is + * canonicalized relatively to the current @tracee's file-system + * name-space. This function returns -errno on error, otherwise 0. + */ +int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final) +{ + int status; + + if (tracee == NULL) + status = (realpath(path, host_path) == NULL ? -errno : 0); + else + status = translate_path(tracee, host_path, AT_FDCWD, path, deref_final); + return status; +} + +/** + * Put in @guest_path the canonicalized current working directory. In + * the nominal case (@tracee == NULL), this function is barely + * equivalent to realpath(), but when doing sub-reconfiguration, the + * path is canonicalized relatively to the current @tracee's + * file-system name-space. This function returns -errno on error, + * otherwise 0. + */ +int getcwd2(Tracee *tracee, char guest_path[PATH_MAX]) +{ + if (tracee == NULL) { + if (getcwd(guest_path, PATH_MAX) == NULL) + return -errno; + } + else + strcpy(guest_path, tracee->fs->cwd); + + return 0; +} + +/** + * Remove the trailing "/" or "/.". + */ +void chop_finality(char *path) +{ + size_t length = strlen(path); + + if (path[length - 1] == '.') { + assert(length >= 2); + /* Special case for "/." */ + if (length == 2) + path[length - 1] = '\0'; + else + path[length - 2] = '\0'; + } + else if (path[length - 1] == '/') { + /* Special case for "/" */ + if (length > 1) + path[length - 1] = '\0'; + } +} + +/** + * Put in @path the result of readlink(/proc/@pid/fd/@fd). This + * function returns -errno if an error occured, otherwise 0. + */ +int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]) +{ + char link[32]; /* 32 > sizeof("/proc//cwd") + sizeof(#ULONG_MAX) */ + int status; + + /* Format the path to the "virtual" link. */ + status = snprintf(link, sizeof(link), "/proc/%d/fd/%d", pid, fd); + if (status < 0) + return -EBADF; + if ((size_t) status >= sizeof(link)) + return -EBADF; + + /* Read the value of this "virtual" link. */ + status = readlink(link, path, PATH_MAX); + if (status < 0) + return -EBADF; + if (status >= PATH_MAX) + return -ENAMETOOLONG; + path[status] = '\0'; + + return 0; +} + +/** + * Copy in @result the equivalent of "@tracee->root + canon(@dir_fd + + * @user_path)". If @user_path is not absolute then it is relative to + * the directory referred by the descriptor @dir_fd (AT_FDCWD is for + * the current working directory). See the documentation of + * canonicalize() for the meaning of @deref_final. This function + * returns -errno if an error occured, otherwise 0. + */ +int translate_path(Tracee *tracee, char result[PATH_MAX], int dir_fd, + const char *user_path, bool deref_final) +{ + char guest_path[PATH_MAX]; + int status; + + /* Use "/" as the base if it is an absolute guest path. */ + if (user_path[0] == '/') { + strcpy(result, "/"); + } + /* It is relative to a directory referred by a descriptor, see + * openat(2) for details. */ + else if (dir_fd != AT_FDCWD) { + /* /proc/@tracee->pid/fd/@dir_fd -> result. */ + status = readlink_proc_pid_fd(tracee->pid, dir_fd, result); + if (status < 0) + return status; + + /* Named file descriptors may reference special + * objects like pipes, sockets, inodes, ... Such + * objects do not belong to the file-system. */ + if (result[0] != '/') + return -ENOTDIR; + + /* Remove the leading "root" part of the base + * (required!). */ + status = detranslate_path(tracee, result, NULL); + if (status < 0) + return status; + } + /* It is relative to the current working directory. */ + else { + status = getcwd2(tracee, result); + if (status < 0) + return status; + } + + VERBOSE(tracee, 2, "pid %d: translate(\"%s\" + \"%s\")", + tracee != NULL ? tracee->pid : 0, result, user_path); + + /* So far "result" was used as a base path, it's time to join + * it to the user path. */ + join_paths(guest_path, result, user_path); + strcpy(result, "/"); + + /* Canonicalize regarding the new root. */ + status = canonicalize(tracee, guest_path, deref_final, result, 0); + if (status < 0) + return status; + + /* Final binding substitution to convert "result" into a host + * path, since canonicalize() works from the guest + * point-of-view. */ + status = substitute_binding(tracee, GUEST, result); + if (status < 0) + return status; + + return 0; +} + +/** + * Remove/substitute the leading part of a "translated" @path. It + * returns 0 if no transformation is required (ie. symmetric binding), + * otherwise it returns the size in bytes of the updated @path, + * including the end-of-string terminator. On error it returns + * -errno. + */ +int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]) +{ + size_t prefix_length; + ssize_t new_length; + + bool sanity_check; + bool follow_binding; + + /* Don't try to detranslate relative paths (typically the + * target of a relative symbolic link). */ + if (path[0] != '/') + return 0; + + /* Is it a symlink? */ + if (t_referrer != NULL) { + Comparison comparison; + + sanity_check = false; + follow_binding = false; + + /* In some cases bindings have to be resolved. */ + comparison = compare_paths("/proc", t_referrer); + if (comparison == PATH1_IS_PREFIX) { + /* Some links in "/proc" are generated + * dynamically by the kernel. PRoot has to + * emulate some of them. */ + char proc_path[PATH_MAX]; + strcpy(proc_path, path); + new_length = readlink_proc2(tracee, proc_path, t_referrer); + if (new_length < 0) + return new_length; + if (new_length != 0) { + strcpy(path, proc_path); + return new_length + 1; + } + + /* Always resolve bindings for symlinks in + * "/proc", they always point to the emulated + * file-system namespace by design. */ + follow_binding = true; + } + else if (!belongs_to_guestfs(tracee, t_referrer)) { + const char *binding_referree; + const char *binding_referrer; + + binding_referree = get_path_binding(tracee, HOST, path); + binding_referrer = get_path_binding(tracee, HOST, t_referrer); + assert(binding_referrer != NULL); + + /* Resolve bindings for symlinks that belong + * to a binding and point to the same binding. + * For example, if "-b /lib:/foo" is specified + * and the symlink "/lib/a -> /lib/b" exists + * in the host rootfs namespace, then it + * should appear as "/foo/a -> /foo/b" in the + * guest rootfs namespace for consistency + * reasons. */ + if (binding_referree != NULL) { + comparison = compare_paths(binding_referree, binding_referrer); + follow_binding = (comparison == PATHS_ARE_EQUAL); + } + } + } + else { + sanity_check = true; + follow_binding = true; + } + + if (follow_binding) { + switch (substitute_binding(tracee, HOST, path)) { + case 0: + return 0; + case 1: + return strlen(path) + 1; + default: + break; + } + } + + switch (compare_paths(get_root(tracee), path)) { + case PATH1_IS_PREFIX: + /* Remove the leading part, that is, the "root". */ + prefix_length = strlen(get_root(tracee)); + + /* Special case when path to the guest rootfs == "/". */ + if (prefix_length == 1) + prefix_length = 0; + + new_length = strlen(path) - prefix_length; + memmove(path, path + prefix_length, new_length); + + path[new_length] = '\0'; + break; + + case PATHS_ARE_EQUAL: + /* Special case when path == root. */ + new_length = 1; + strcpy(path, "/"); + break; + + default: + /* Ensure the path is within the new root. */ + if (sanity_check) + return -EPERM; + else + return 0; + } + + return new_length + 1; +} + +/** + * Check if the translated @host_path belongs to the guest rootfs, + * that is, isn't from a binding. + */ +bool belongs_to_guestfs(const Tracee *tracee, const char *host_path) +{ + Comparison comparison; + + comparison = compare_paths(get_root(tracee), host_path); + return (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX); +} + +/** + * Compare @path1 with @path2, which are respectively @length1 and + * @length2 long. + * + * This function works only with paths canonicalized in the same + * namespace (host/guest)! + */ +Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2) +{ + size_t length_min; + bool is_prefix; + char sentinel; + + if (!length1 || !length2) + return PATHS_ARE_NOT_COMPARABLE; + + /* Remove potential trailing '/' for the comparison. */ + if (path1[length1 - 1] == '/') + length1--; + + if (path2[length2 - 1] == '/') + length2--; + + if (length1 < length2) { + length_min = length1; + sentinel = path2[length_min]; + } + else { + length_min = length2; + sentinel = path1[length_min]; + } + + /* Optimize obvious cases. */ + if (sentinel != '/' && sentinel != '\0') + return PATHS_ARE_NOT_COMPARABLE; + + is_prefix = (strncmp(path1, path2, length_min) == 0); + + if (!is_prefix) + return PATHS_ARE_NOT_COMPARABLE; + + if (length1 == length2) + return PATHS_ARE_EQUAL; + else if (length1 < length2) + return PATH1_IS_PREFIX; + else if (length1 > length2) + return PATH2_IS_PREFIX; + + return PATHS_ARE_NOT_COMPARABLE; +} + +Comparison compare_paths(const char *path1, const char *path2) +{ + return compare_paths2(path1, strlen(path1), path2, strlen(path2)); +} + +typedef int (*foreach_fd_t)(const Tracee *tracee, int fd, char path[PATH_MAX]); + +/** + * Call @callback on each open file descriptors of @pid. It returns + * the status of the first failure, that is, if @callback returned + * seomthing lesser than 0, otherwise 0. + */ +static int foreach_fd(const Tracee *tracee, foreach_fd_t callback) +{ + struct dirent *dirent; + char path[PATH_MAX]; + char proc_fd[32]; /* 32 > sizeof("/proc//fd") + sizeof(#ULONG_MAX) */ + int status; + DIR *dirp; + + /* Format the path to the "virtual" directory. */ + status = snprintf(proc_fd, sizeof(proc_fd), "/proc/%d/fd", tracee->pid); + if (status < 0 || (size_t) status >= sizeof(proc_fd)) + return 0; + + /* Open the virtual directory "/proc/$pid/fd". */ + dirp = opendir(proc_fd); + if (dirp == NULL) + return 0; + + while ((dirent = readdir(dirp)) != NULL) { + /* Read the value of this "virtual" link. Don't use + * readlinkat(2) here since it would require Linux >= + * 2.6.16 and Glibc >= 2.4, whereas PRoot is supposed + * to work on any Linux 2.6 systems. */ + + char tmp[PATH_MAX]; + if (strlen(proc_fd) + strlen(dirent->d_name) + 1 >= PATH_MAX) + continue; + + strcpy(tmp, proc_fd); + strcat(tmp, "/"); + strcat(tmp, dirent->d_name); + + status = readlink(tmp, path, PATH_MAX); + if (status < 0 || status >= PATH_MAX) + continue; + path[status] = '\0'; + + /* Ensure it points to a path (not a socket or somethink like that). */ + if (path[0] != '/') + continue; + + status = callback(tracee, atoi(dirent->d_name), path); + if (status < 0) + goto end; + } + status = 0; + +end: + closedir(dirp); + return status; +} + +/** + * Helper for list_open_fd(). + */ +static int list_open_fd_callback(const Tracee *tracee, int fd, char path[PATH_MAX]) +{ + VERBOSE(tracee, 1, "pid %d: access to \"%s\" (fd %d) won't be translated until closed", + tracee->pid, path, fd); + return 0; +} + +/** + * Warn for files that are open. It is useful right after PRoot has + * attached a process. + */ +int list_open_fd(const Tracee *tracee) +{ + return foreach_fd(tracee, list_open_fd_callback); +} + +/** + * Substitute the first @old_prefix_length bytes of @path with + * @new_prefix (the caller has to provides a correct + * @new_prefix_length). This function returns the new length of + * @path. Note: this function takes care about special cases (like + * "/"). + */ +size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length, + const char *new_prefix, size_t new_prefix_length) +{ + size_t path_length; + size_t new_length; + + path_length = strlen(path); + + assert(old_prefix_length < PATH_MAX); + assert(new_prefix_length < PATH_MAX); + + if (new_prefix_length == 1) { + /* Special case: "/foo" -> "/". Substitute "/foo/bin" + * with "/bin" not "//bin". */ + + new_length = path_length - old_prefix_length; + if (new_length != 0) + memmove(path, path + old_prefix_length, new_length); + else { + /* Special case: "/". */ + path[0] = '/'; + new_length = 1; + } + } + else if (old_prefix_length == 1) { + /* Special case: "/" -> "/foo". Substitute "/bin" with + * "/foo/bin" not "/foobin". */ + + new_length = new_prefix_length + path_length; + if (new_length >= PATH_MAX) + return -ENAMETOOLONG; + + if (path_length > 1) { + memmove(path + new_prefix_length, path, path_length); + memcpy(path, new_prefix, new_prefix_length); + } + else { + /* Special case: "/". */ + memcpy(path, new_prefix, new_prefix_length); + new_length = new_prefix_length; + } + } + else { + /* Generic case. */ + + new_length = path_length - old_prefix_length + new_prefix_length; + if (new_length >= PATH_MAX) + return -ENAMETOOLONG; + + memmove(path + new_prefix_length, + path + old_prefix_length, + path_length - old_prefix_length); + memcpy(path, new_prefix, new_prefix_length); + } + + assert(new_length < PATH_MAX); + path[new_length] = '\0'; + + return new_length; +} diff --git a/app/src/main/cpp/proot/src/path/path.h b/app/src/main/cpp/proot/src/path/path.h new file mode 100644 index 0000000..4f53570 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/path.h @@ -0,0 +1,99 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef PATH_H +#define PATH_H + +#include /* pid_t, */ +#include /* AT_FDCWD, */ +#include /* PATH_MAX, */ +#include + +#include "tracee/tracee.h" + +/* File type. */ +typedef enum { + REGULAR, + SYMLINK, +} Type; + +/* Path point-of-view. */ +typedef enum { + GUEST, + HOST, + + /* Used for bindings as specified by the user but not + * canonicalized yet (new_binding, initialize_binding). */ + PENDING, +} Side; + +/* Path with cached attributes. */ +typedef struct { + char path[PATH_MAX]; + size_t length; + Side side; +} Path; + +/* Path ending type. */ +typedef enum { + NOT_FINAL, + FINAL_NORMAL, + FINAL_SLASH, + FINAL_DOT +} Finality; + +#define IS_FINAL(a) ((a) != NOT_FINAL) + +/* Comparison between two paths. */ +typedef enum Comparison { + PATHS_ARE_EQUAL, + PATH1_IS_PREFIX, + PATH2_IS_PREFIX, + PATHS_ARE_NOT_COMPARABLE, +} Comparison; + +extern int which(Tracee *tracee, const char *paths, char host_path[PATH_MAX], const char *command); +extern int realpath2(Tracee *tracee, char host_path[PATH_MAX], const char *path, bool deref_final); +extern int getcwd2(Tracee *tracee, char guest_path[PATH_MAX]); +extern void chop_finality(char *path); + +extern int translate_path(Tracee *tracee, char host_path[PATH_MAX], + int dir_fd, const char *guest_path, bool deref_final); + +extern int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]); +extern bool belongs_to_guestfs(const Tracee *tracee, const char *path); + +extern void join_paths(char result[PATH_MAX], const char *path1, const char *path2); +extern int list_open_fd(const Tracee *tracee); + +extern Comparison compare_paths(const char *path1, const char *path2); +extern Comparison compare_paths2(const char *path1, size_t length1, const char *path2, size_t length2); + +extern size_t substitute_path_prefix(char path[PATH_MAX], size_t old_prefix_length, + const char *new_prefix, size_t new_prefix_length); + +extern int readlink_proc_pid_fd(pid_t pid, int fd, char path[PATH_MAX]); + +/* Check if path interpretable relatively to dirfd, see openat(2) for details. */ +#define AT_FD(dirfd, path) ((dirfd) != AT_FDCWD && ((path) != NULL && (path)[0] != '/')) + +#endif /* PATH_H */ diff --git a/app/src/main/cpp/proot/src/path/proc.c b/app/src/main/cpp/proot/src/path/proc.c new file mode 100644 index 0000000..d0f2566 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/proc.c @@ -0,0 +1,189 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* snprintf(3), */ +#include /* strcmp(3), */ +#include /* atoi(3), strtol(3), */ +#include /* E*, */ +#include /* assert(3), */ + +#include "path/proc.h" +#include "tracee/tracee.h" +#include "path/path.h" +#include "path/binding.h" + +/** + * This function emulates the @result of readlink("@base/@component") + * with respect to @tracee, where @base belongs to "/proc" (according + * to @comparison). This function returns -errno on error, an enum + * @action otherwise (c.f. above). + * + * Unlike readlink(), this function includes the nul terminating byte + * to @result. + */ +Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], + const char base[PATH_MAX], const char component[NAME_MAX], + Comparison comparison) +{ + const Tracee *known_tracee; + char proc_path[64]; /* 64 > sizeof("/proc//fd/") + 2 * sizeof(#ULONG_MAX) */ + int status; + pid_t pid; + + /* Remember: comparison = compare_paths("/proc", base) */ + switch (comparison) { + case PATHS_ARE_EQUAL: + /* Substitute "/proc/self" with "/proc/". */ + if (strcmp(component, "self") != 0) + return DEFAULT; + + status = snprintf(result, PATH_MAX, "/proc/%d", tracee->pid); + if (status < 0 || status >= PATH_MAX) + return -EPERM; + + return CANONICALIZE; + + case PATH1_IS_PREFIX: + /* Handle "/proc/" below, where is process + * monitored by PRoot. */ + break; + + default: + return DEFAULT; + } + + pid = atoi(base + strlen("/proc/")); + if (pid == 0) + return DEFAULT; + + /* Handle links in "/proc//". */ + status = snprintf(proc_path, sizeof(proc_path), "/proc/%d", pid); + if (status < 0 || (size_t) status >= sizeof(proc_path)) + return -EPERM; + + comparison = compare_paths(proc_path, base); + switch (comparison) { + case PATHS_ARE_EQUAL: + known_tracee = get_tracee(tracee, pid, false); + if (known_tracee == NULL) + return DEFAULT; + +#define SUBSTITUTE(name, string) \ + do { \ + if (strcmp(component, #name) != 0) \ + break; \ + \ + status = strlen(string); \ + if (status >= PATH_MAX) \ + return -EPERM; \ + \ + strncpy(result, string, status + 1); \ + return CANONICALIZE; \ + } while (0) + + /* Substitute link "/proc//???" with the content + * of tracee->???. */ + SUBSTITUTE(exe, known_tracee->exe); + SUBSTITUTE(cwd, known_tracee->fs->cwd); + SUBSTITUTE(root, get_root(known_tracee)); +#undef SUBSTITUTE + return DEFAULT; + + case PATH1_IS_PREFIX: + /* Handle "/proc//???" below. */ + break; + + default: + return DEFAULT; + } + + /* Handle links in "/proc//fd/". */ + status = snprintf(proc_path, sizeof(proc_path), "/proc/%d/fd", pid); + if (status < 0 || (size_t) status >= sizeof(proc_path)) + return -EPERM; + + comparison = compare_paths(proc_path, base); + switch (comparison) { + char *end_ptr; + + case PATHS_ARE_EQUAL: + /* Sanity check: a number is expected. */ + errno = 0; + (void) strtol(component, &end_ptr, 10); + if (errno != 0 || end_ptr == component) + return -EPERM; + + /* Don't dereference "/proc//fd/???" now: they + * can point to anonymous pipe, socket, ... otherwise + * they point to a path already canonicalized by the + * kernel. + * + * Note they are still correctly detranslated in + * syscall/exit.c if a monitored process uses + * readlink() against any of them. */ + status = snprintf(result, PATH_MAX, "%s/%s", base, component); + if (status < 0 || status >= PATH_MAX) + return -EPERM; + + return DONT_CANONICALIZE; + + default: + break; + } + + return DEFAULT; +} + +/** + * This function emulates the @result of readlink("@referer") with + * respect to @tracee, where @referer is a strict subpath of "/proc". + * This function returns -errno if an error occured, the length of + * @result if the readlink was emulated, 0 otherwise. + * + * Unlike readlink(), this function includes the nul terminating byte + * to @result (but this byte is not counted in the returned value). + */ +ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char referer[PATH_MAX]) +{ + Action action; + char base[PATH_MAX]; + char *component; + + assert(compare_paths("/proc", referer) == PATH1_IS_PREFIX); + + /* It's safe to use strrchr() here since @referer was + * previously canonicalized. */ + strcpy(base, referer); + component = strrchr(base, '/'); + + /* These cases are not possible: @referer is supposed to be a + * canonicalized subpath of "/proc". */ + assert(component != NULL && component != base); + + component[0] = '\0'; + component++; + if (component[0] == '\0') + return 0; + + action = readlink_proc(tracee, result, base, component, PATH1_IS_PREFIX); + return (action == CANONICALIZE ? strlen(result) : 0); +} diff --git a/app/src/main/cpp/proot/src/path/proc.h b/app/src/main/cpp/proot/src/path/proc.h new file mode 100644 index 0000000..7081dd7 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/proc.h @@ -0,0 +1,44 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef PROC_H +#define PROC_H + +#include + +#include "tracee/tracee.h" +#include "path/path.h" + +/* Action to do after a call to readlink_proc(). */ +typedef enum { + DEFAULT, /* Nothing special to do, treat it as a regular link. */ + CANONICALIZE, /* The symlink was dereferenced, now canonicalize it. */ + DONT_CANONICALIZE, /* The symlink shouldn't be dereferenced nor canonicalized. */ +} Action; + + +extern Action readlink_proc(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX], + const char component[NAME_MAX], Comparison comparison); + +extern ssize_t readlink_proc2(const Tracee *tracee, char result[PATH_MAX], const char path[PATH_MAX]); + +#endif /* PROC_H */ diff --git a/app/src/main/cpp/proot/src/path/temp.c b/app/src/main/cpp/proot/src/path/temp.c new file mode 100644 index 0000000..b300aff --- /dev/null +++ b/app/src/main/cpp/proot/src/path/temp.c @@ -0,0 +1,354 @@ +#include /* stat(2), opendir(3), */ +#include /* stat(2), chmod(2), */ +#include /* stat(2), rmdir(2), unlink(2), readlink(2), */ +#include /* errno(2), */ +#include /* readdir(3), opendir(3), */ +#include /* strcmp(3), */ +#include /* free(3), getenv(3), */ +#include /* P_tmpdir, */ +#include /* talloc(3), */ + +#include "cli/note.h" + +/** + * Return the path to a directory where temporary files should be + * created. + */ +const char *get_temp_directory() +{ + static const char *temp_directory = NULL; + char *tmp; + + if (temp_directory != NULL) + return temp_directory; + + temp_directory = getenv("PROOT_TMP_DIR"); + if (temp_directory == NULL) { + temp_directory = P_tmpdir; + return temp_directory; + } + + tmp = realpath(temp_directory, NULL); + if (tmp == NULL) { + note(NULL, WARNING, SYSTEM, + "can't canonicalize %s, using %s instead of PROOT_TMP_DIR", + temp_directory, P_tmpdir); + + temp_directory = P_tmpdir; + return temp_directory; + } + + temp_directory = talloc_strdup(talloc_autofree_context(), tmp); + if (temp_directory == NULL) + temp_directory = tmp; + else + free(tmp); + + return temp_directory; +} + +/** + * Handle the return of d_type = DT_UNKNOWN by readdir(3) + * Not all filesystems support returning d_type in readdir(3) + */ +static int get_dtype(struct dirent *de) +{ + int dtype = de ? de->d_type : DT_UNKNOWN; + struct stat st; + + if (dtype != DT_UNKNOWN) + return dtype; + if (lstat(de->d_name, &st)) + return dtype; + if (S_ISREG(st.st_mode)) + return DT_REG; + if (S_ISDIR(st.st_mode)) + return DT_DIR; + if (S_ISLNK(st.st_mode)) + return DT_LNK; + return dtype; +} + +/** + * Remove recursively the content of the current working directory. + * This latter has to lie in temp_directory (ie. "/tmp" on most + * systems). This function returns -1 if a fatal error occured + * (ie. the recursion must be stopped), the number of non-fatal errors + * otherwise. + * + * WARNING: this function changes the current working directory for + * the calling process. + */ +static int clean_temp_cwd() +{ + const char *temp_directory = get_temp_directory(); + const size_t length_temp_directory = strlen(temp_directory); + char *prefix = NULL; + int nb_errors = 0; + DIR *dir = NULL; + int status; + + prefix = talloc_size(NULL, length_temp_directory + 1); + if (prefix == NULL) { + note(NULL, WARNING, INTERNAL, "can't allocate memory"); + nb_errors++; + goto end; + } + + /* Sanity check: ensure the current directory lies in + * "/tmp". */ + status = readlink("/proc/self/cwd", prefix, length_temp_directory); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't readlink '/proc/self/cwd'"); + nb_errors++; + goto end; + } + prefix[status] = '\0'; + + if (strncmp(prefix, temp_directory, length_temp_directory) != 0) { + note(NULL, ERROR, INTERNAL, + "trying to remove a directory outside of '%s', " + "please report this error.\n", temp_directory); + nb_errors++; + goto end; + } + + dir = opendir("."); + if (dir == NULL) { + note(NULL, WARNING, SYSTEM, "can't open '.'"); + nb_errors++; + goto end; + } + + while (1) { + struct dirent *entry; + + errno = 0; + entry = readdir(dir); + if (entry == NULL) + break; + + if ( strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) + continue; + + status = chmod(entry->d_name, 0700); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "cant chmod '%s'", entry->d_name); + nb_errors++; + continue; + } + + if (get_dtype(entry) == DT_DIR) { + status = chdir(entry->d_name); + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't chdir '%s'", entry->d_name); + nb_errors++; + continue; + } + + /* Recurse. */ + status = clean_temp_cwd(); + if (status < 0) { + nb_errors = -1; + goto end; + } + nb_errors += status; + + status = chdir(".."); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chdir to '..'"); + nb_errors = -1; + goto end; + } + + status = rmdir(entry->d_name); + } + else { + status = unlink(entry->d_name); + } + if (status < 0) { + note(NULL, WARNING, SYSTEM, "can't remove '%s'", entry->d_name); + nb_errors++; + continue; + } + } + if (errno != 0) { + note(NULL, WARNING, SYSTEM, "can't readdir '.'"); + nb_errors++; + } + +end: + TALLOC_FREE(prefix); + + if (dir != NULL) + (void) closedir(dir); + + return nb_errors; +} + +/** + * Remove recursively @path. This latter has to be a directory lying + * in temp_directory (ie. "/tmp" on most systems). This function + * returns -1 on error, otherwise 0. + */ +static int remove_temp_directory2(const char *path) +{ + int result; + int status; + char *cwd; + + cwd = malloc(PATH_MAX); + getcwd(cwd, PATH_MAX); + + status = chmod(path, 0700); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chmod '%s'", path); + result = -1; + goto end; + } + + status = chdir(path); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chdir to '%s'", path); + result = -1; + goto end; + } + + status = clean_temp_cwd(); + result = (status == 0 ? 0 : -1); + + /* Try to remove path even if something went wrong. */ + status = chdir(".."); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "can't chdir to '..'"); + result = -1; + goto end; + } + + status = rmdir(path); + if (status < 0) { + note(NULL, ERROR, SYSTEM, "cant remove '%s'", path); + result = -1; + goto end; + } + +end: + if (cwd != NULL) { + status = chdir(cwd); + if (status < 0) { + result = -1; + note(NULL, ERROR, SYSTEM, "can't chdir to '%s'", cwd); + } + free(cwd); + } + + return result; +} + +/** + * Like remove_temp_directory2() but always return 0. + * + * Note: this is a talloc destructor. + */ +static int remove_temp_directory(char *path) +{ + (void) remove_temp_directory2(path); + return 0; +} + +/** + * Remove the file @path. This function always returns 0. + * + * Note: this is a talloc destructor. + */ +static int remove_temp_file(char *path) +{ + int status; + + status = unlink(path); + if (status < 0) + note(NULL, ERROR, SYSTEM, "can't remove '%s'", path); + + return 0; +} + +/** + * Create a path name with the following format: + * "/tmp/@prefix-$PID-XXXXXX". The returned C string is either + * auto-freed if @context is NULL. This function returns NULL if an + * error occurred. + */ +char *create_temp_name(TALLOC_CTX *context, const char *prefix) +{ + const char *temp_directory = get_temp_directory(); + char *name; + + if (context == NULL) + context = talloc_autofree_context(); + + name = talloc_asprintf(context, "%s/%s-%d-XXXXXX", temp_directory, prefix, getpid()); + if (name == NULL) { + note(NULL, ERROR, INTERNAL, "can't allocate memory"); + return NULL; + } + + return name; +} + +/** + * Create a directory that will be automatically removed either on + * PRoot termination if @context is NULL, or once its path name + * (attached to @context) is freed. This function returns NULL on + * error, otherwise the absolute path name to the created directory + * (@prefix-ed). + */ +const char *create_temp_directory(TALLOC_CTX *context, const char *prefix) +{ + char *name; + + name = create_temp_name(context, prefix); + if (name == NULL) + return NULL; + + name = mkdtemp(name); + if (name == NULL) { + note(NULL, ERROR, SYSTEM, "can't create temporary directory"); + note(NULL, INFO, USER, "Please set PROOT_TMP_DIR env. variable " + "to an alternate location (with write permission)."); + return NULL; + } + + talloc_set_destructor(name, remove_temp_directory); + + return name; +} + +/** + * Create a file that will be automatically removed either on PRoot + * termination if @context is NULL, or once its path name (attached to + * @context) is freed. This function returns NULL on error, + * otherwise the absolute path name to the created file (@prefix-ed). + */ +const char *create_temp_file(TALLOC_CTX *context, const char *prefix) +{ + char *name; + int fd; + + name = create_temp_name(context, prefix); + if (name == NULL) + return NULL; + + fd = mkstemp(name); + if (fd < 0) { + note(NULL, ERROR, SYSTEM, "can't create temporary file"); + note(NULL, INFO, USER, "Please set PROOT_TMP_DIR env. variable " + "to an alternate location (with write permission)."); + return NULL; + } + close(fd); + + talloc_set_destructor(name, remove_temp_file); + + return name; +} diff --git a/app/src/main/cpp/proot/src/path/temp.h b/app/src/main/cpp/proot/src/path/temp.h new file mode 100644 index 0000000..5315f44 --- /dev/null +++ b/app/src/main/cpp/proot/src/path/temp.h @@ -0,0 +1,33 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef TEMP_H +#define TEMP_H + +#include + +extern char *create_temp_name(TALLOC_CTX *context, const char *prefix); +extern const char *create_temp_directory(TALLOC_CTX *context, const char *prefix); +extern const char *create_temp_file(TALLOC_CTX *context, const char *prefix); +extern const char *get_temp_directory(); + +#endif /* TEMP_H */ diff --git a/app/src/main/cpp/proot/src/ptrace/ptrace.c b/app/src/main/cpp/proot/src/ptrace/ptrace.c new file mode 100644 index 0000000..64902d6 --- /dev/null +++ b/app/src/main/cpp/proot/src/ptrace/ptrace.c @@ -0,0 +1,575 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* PTRACE_*, */ +#include /* E*, */ +#include /* assert(3), */ +#include /* bool, true, false, */ +#include /* siginfo_t, */ +#include /* struct iovec, */ +#include /* MIN(), MAX(), */ +#include /* __WALL, */ +#include /* memcpy(3), */ +#include /* bzero(3), */ + +#include "ptrace/ptrace.h" +#include "tracee/tracee.h" +#include "syscall/sysnum.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "tracee/event.h" +#include "cli/note.h" +#include "arch.h" + +#include "compat.h" + +#if defined(ARCH_ARM_EABI) +#define user_fpregs_struct user_fpregs +#endif + +#if defined(ARCH_ARM64) +#define user_fpregs_struct user_fpsimd_struct +#endif + +static const char *stringify_ptrace(int request) +{ +#define CASE_STR(a) case a: return #a; break; + switch ((int) request) { + CASE_STR(PTRACE_TRACEME) CASE_STR(PTRACE_PEEKTEXT) CASE_STR(PTRACE_PEEKDATA) + CASE_STR(PTRACE_PEEKUSER) CASE_STR(PTRACE_POKETEXT) CASE_STR(PTRACE_POKEDATA) + CASE_STR(PTRACE_POKEUSER) CASE_STR(PTRACE_CONT) CASE_STR(PTRACE_KILL) + CASE_STR(PTRACE_SINGLESTEP) CASE_STR(PTRACE_GETREGS) CASE_STR(PTRACE_SETREGS) + CASE_STR(PTRACE_GETFPREGS) CASE_STR(PTRACE_SETFPREGS) CASE_STR(PTRACE_ATTACH) + CASE_STR(PTRACE_DETACH) CASE_STR(PTRACE_GETFPXREGS) CASE_STR(PTRACE_SETFPXREGS) + CASE_STR(PTRACE_SYSCALL) CASE_STR(PTRACE_SETOPTIONS) CASE_STR(PTRACE_GETEVENTMSG) + CASE_STR(PTRACE_GETSIGINFO) CASE_STR(PTRACE_SETSIGINFO) CASE_STR(PTRACE_GETREGSET) + CASE_STR(PTRACE_SETREGSET) CASE_STR(PTRACE_SEIZE) CASE_STR(PTRACE_INTERRUPT) + CASE_STR(PTRACE_LISTEN) CASE_STR(PTRACE_SET_SYSCALL) + CASE_STR(PTRACE_GET_THREAD_AREA) CASE_STR(PTRACE_SET_THREAD_AREA) + CASE_STR(PTRACE_GETVFPREGS) CASE_STR(PTRACE_SINGLEBLOCK) CASE_STR(PTRACE_ARCH_PRCTL) + default: return "PTRACE_???"; } +} + +/** + * Translate the ptrace syscall made by @tracee into a "void" syscall + * in order to emulate the ptrace mechanism within PRoot. This + * function returns -errno if an error occured (unsupported request), + * otherwise 0. + */ +int translate_ptrace_enter(Tracee *tracee) +{ + /* The ptrace syscall have to be emulated since it can't be nested. */ + set_sysnum(tracee, PR_void); + return 0; +} + +/** + * Set @ptracee's tracer to @ptracer, and increment ptracees counter + * of this later. + */ +void attach_to_ptracer(Tracee *ptracee, Tracee *ptracer) +{ + bzero(&(PTRACEE), sizeof(PTRACEE)); + PTRACEE.ptracer = ptracer; + + PTRACER.nb_ptracees++; +} + +/** + * Unset @ptracee's tracer, and decrement ptracees counter of this + * later. + */ +void detach_from_ptracer(Tracee *ptracee) +{ + Tracee *ptracer = PTRACEE.ptracer; + + PTRACEE.ptracer = NULL; + + assert(PTRACER.nb_ptracees > 0); + PTRACER.nb_ptracees--; +} + +/** + * Emulate the ptrace syscall made by @tracee. This function returns + * -errno if an error occured (unsupported request), otherwise 0. + */ +int translate_ptrace_exit(Tracee *tracee) +{ + word_t request, pid, address, data, result; + Tracee *ptracee, *ptracer; + int forced_signal = -1; + int signal; + int status; + + /* Read ptrace parameters. */ + request = peek_reg(tracee, ORIGINAL, SYSARG_1); + pid = peek_reg(tracee, ORIGINAL, SYSARG_2); + address = peek_reg(tracee, ORIGINAL, SYSARG_3); + data = peek_reg(tracee, ORIGINAL, SYSARG_4); + + /* Propagate signedness for this special value. */ + if (is_32on64_mode(tracee) && pid == 0xFFFFFFFF) + pid = (word_t) -1; + + /* The TRACEME request is the only one used by a tracee. */ + if (request == PTRACE_TRACEME) { + ptracer = tracee->parent; + ptracee = tracee; + + /* The emulated ptrace in PRoot has the same + * limitation as the real ptrace in the Linux kernel: + * only one tracer per process. */ + if (PTRACEE.ptracer != NULL || ptracee == ptracer) + return -EPERM; + + attach_to_ptracer(ptracee, ptracer); + + /* Detect when the ptracer has gone to wait before the + * ptracee did the ptrace(ATTACHME) request. */ + if (PTRACER.waits_in == WAITS_IN_KERNEL) { + status = kill(ptracer->pid, SIGSTOP); + if (status < 0) + note(tracee, WARNING, INTERNAL, + "can't wake ptracer %d", ptracer->pid); + else { + ptracer->sigstop = SIGSTOP_IGNORED; + PTRACER.waits_in = WAITS_IN_PROOT; + } + } + + /* Disable seccomp acceleration for this tracee and + * all its children since we can't assume what are the + * syscalls its tracer is interested with. */ + if (tracee->seccomp == ENABLED) + tracee->seccomp = DISABLING; + + return 0; + } + + /* The ATTACH, SEIZE, and INTERRUPT requests are the only ones + * where the ptracee is in an unknown state. */ + if (request == PTRACE_ATTACH) { + ptracer = tracee; + ptracee = get_tracee(ptracer, pid, false); + if (ptracee == NULL) + return -ESRCH; + + /* The emulated ptrace in PRoot has the same + * limitation as the real ptrace in the Linux kernel: + * only one tracer per process. */ + if (PTRACEE.ptracer != NULL || ptracee == ptracer) + return -EPERM; + + attach_to_ptracer(ptracee, ptracer); + + /* The tracee is sent a SIGSTOP, but will not + * necessarily have stopped by the completion of this + * call. + * + * -- man 2 ptrace. */ + kill(pid, SIGSTOP); + + return 0; + } + + /* Here, the tracee is a ptracer. Also, the requested ptracee + * has to be in the "stopped for ptracer" state. */ + ptracer = tracee; + ptracee = get_stopped_ptracee(ptracer, pid, false, __WALL); + if (ptracee == NULL) { + static bool warned = false; + + /* Ensure we didn't get there only because inheritance + * mechanism has missed this one. */ + ptracee = get_tracee(tracee, pid, false); + if (ptracee != NULL && ptracee->exe == NULL && !warned) { + warned = true; + note(ptracer, WARNING, INTERNAL, "ptrace request to an unexpected ptracee"); + } + + return -ESRCH; + } + + /* Sanity checks. */ + if ( PTRACEE.is_zombie + || PTRACEE.ptracer != ptracer + || pid == (word_t) -1) + return -ESRCH; + + switch (request) { + case PTRACE_SYSCALL: + PTRACEE.ignore_syscalls = false; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_CONT: + PTRACEE.ignore_syscalls = true; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_SINGLESTEP: + ptracee->restart_how = PTRACE_SINGLESTEP; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_SINGLEBLOCK: + ptracee->restart_how = PTRACE_SINGLEBLOCK; + forced_signal = (int) data; + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_DETACH: + detach_from_ptracer(ptracee); + status = 0; + break; /* Restart the ptracee. */ + + case PTRACE_KILL: + status = ptrace(request, pid, NULL, NULL); + break; /* Restart the ptracee. */ + + case PTRACE_SETOPTIONS: + PTRACEE.options = data; + return 0; /* Don't restart the ptracee. */ + + case PTRACE_GETEVENTMSG: { + status = ptrace(request, pid, NULL, &result); + if (status < 0) + return -errno; + + poke_word(ptracer, data, result); + if (errno != 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_PEEKUSER: + if (is_32on64_mode(ptracer) && address == (word_t) -1) + return -EIO; + /* Fall through. */ + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + errno = 0; + result = (word_t) ptrace(request, pid, address, NULL); + if (errno != 0) + return -errno; + + poke_word(ptracer, data, result); + if (errno != 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + case PTRACE_POKEUSER: + if (is_32on64_mode(ptracer)) { + if (address == (word_t) -1) + return -EIO; + } + + status = ptrace(request, pid, address, data); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + if (is_32on64_mode(ptracer)) { + word_t tmp; + + errno = 0; + tmp = (word_t) ptrace(PTRACE_PEEKDATA, ptracee->pid, address, NULL); + if (errno != 0) + return -errno; + + data |= (tmp & 0xFFFFFFFF00000000ULL); + } + + status = ptrace(request, pid, address, data); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + case PTRACE_GETSIGINFO: { + siginfo_t siginfo; + + status = ptrace(request, pid, NULL, &siginfo); + if (status < 0) + return -errno; + + status = write_data(ptracer, data, &siginfo, sizeof(siginfo)); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETSIGINFO: { + siginfo_t siginfo; + + status = read_data(ptracer, &siginfo, data, sizeof(siginfo)); + if (status < 0) + return status; + + status = ptrace(request, pid, NULL, &siginfo); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETREGS: { + size_t size; + union { + struct user_regs_struct regs; + uint32_t regs32[0]; + } buffer; + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + if (is_32on64_mode(tracee)) { + struct user_regs_struct regs64; + + memcpy(®s64, &buffer.regs, sizeof(struct user_regs_struct)); + + size = sizeof(buffer.regs32); + } + else + size = sizeof(buffer.regs); + + status = write_data(ptracer, data, &buffer, size); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETREGS: { + size_t size; + union { + struct user_regs_struct regs; + uint32_t regs32[0]; + } buffer; + + size = (is_32on64_mode(ptracer) + ? sizeof(buffer.regs32) + : sizeof(buffer.regs)); + + status = read_data(ptracer, &buffer, data, size); + if (status < 0) + return status; + + if (is_32on64_mode(ptracer)) { + uint32_t regs32[0]; + + memcpy(regs32, buffer.regs32, sizeof(regs32)); + } + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETFPREGS: { + size_t size; + union { + struct user_fpregs_struct fpregs; + uint32_t fpregs32[0]; + } buffer; + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + if (is_32on64_mode(tracee)) { + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, + "ptrace 32-bit request '%s' not supported on 64-bit yet", + stringify_ptrace(request)); + warned = true; + bzero(&buffer, sizeof(buffer)); + size = sizeof(buffer.fpregs32); + } + else + size = sizeof(buffer.fpregs); + + status = write_data(ptracer, data, &buffer, size); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETFPREGS: { + size_t size; + union { + struct user_fpregs_struct fpregs; + uint32_t fpregs32[0]; + } buffer; + + size = (is_32on64_mode(ptracer) + ? sizeof(buffer.fpregs32) + : sizeof(buffer.fpregs)); + + status = read_data(ptracer, &buffer, data, size); + if (status < 0) + return status; + + if (is_32on64_mode(ptracer)) { + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, + "ptrace 32-bit request '%s' not supported on 64-bit yet", + stringify_ptrace(request)); + warned = true; + return -ENOTSUP; + } + + status = ptrace(request, pid, NULL, &buffer); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETREGSET: { + struct iovec local_iovec; + word_t remote_iovec_base; + word_t remote_iovec_len; + + remote_iovec_base = peek_word(ptracer, data); + if (errno != 0) + return -errno; + + remote_iovec_len = peek_word(ptracer, data + sizeof_word(ptracer)); + if (errno != 0) + return -errno; + + /* Sanity check. */ + assert(sizeof(local_iovec.iov_len) == sizeof(word_t)); + + local_iovec.iov_len = remote_iovec_len; + local_iovec.iov_base = talloc_zero_size(ptracer->ctx, remote_iovec_len); + if (local_iovec.iov_base == NULL) + return -ENOMEM; + + status = ptrace(PTRACE_GETREGSET, pid, address, &local_iovec); + if (status < 0) + return status; + + remote_iovec_len = local_iovec.iov_len = + MIN(remote_iovec_len, local_iovec.iov_len); + + /* Update remote vector content. */ + status = writev_data(ptracer, remote_iovec_base, &local_iovec, 1); + if (status < 0) + return status; + + /* Update remote vector length. */ + poke_word(ptracer, data + sizeof_word(ptracer), remote_iovec_len); + if (errno != 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_SETREGSET: { + struct iovec local_iovec; + word_t remote_iovec_base; + word_t remote_iovec_len; + + remote_iovec_base = peek_word(ptracer, data); + if (errno != 0) + return -errno; + + remote_iovec_len = peek_word(ptracer, data + sizeof_word(ptracer)); + if (errno != 0) + return -errno; + + /* Sanity check. */ + assert(sizeof(local_iovec.iov_len) == sizeof(word_t)); + + local_iovec.iov_len = remote_iovec_len; + local_iovec.iov_base = talloc_zero_size(ptracer->ctx, remote_iovec_len); + if (local_iovec.iov_base == NULL) + return -ENOMEM; + + /* Copy remote content into the local vector. */ + status = read_data(ptracer, local_iovec.iov_base, + remote_iovec_base, local_iovec.iov_len); + if (status < 0) + return status; + + status = ptrace(PTRACE_SETREGSET, pid, address, &local_iovec); + if (status < 0) + return status; + + return 0; /* Don't restart the ptracee. */ + } + + case PTRACE_GETVFPREGS: + case PTRACE_GETFPXREGS: { + static bool warned = false; + if (!warned) + note(ptracer, WARNING, INTERNAL, "ptrace request '%s' not supported yet", + stringify_ptrace(request)); + warned = true; + return -ENOTSUP; + } + + case PTRACE_SET_SYSCALL: + status = ptrace(request, pid, address, data); + if (status < 0) + return -errno; + + return 0; /* Don't restart the ptracee. */ + + default: + note(ptracer, WARNING, INTERNAL, "ptrace request '%s' not supported yet", + stringify_ptrace(request)); + return -ENOTSUP; + } + + /* Now, the initial tracee's event can be handled. */ + signal = PTRACEE.event4.proot.pending + ? handle_tracee_event(ptracee, PTRACEE.event4.proot.value) + : PTRACEE.event4.proot.value; + + /* The restarting signal from the ptracer overrides the + * restarting signal from PRoot. */ + if (forced_signal != -1) + signal = forced_signal; + + (void) restart_tracee(ptracee, signal); + + return status; +} diff --git a/app/src/main/cpp/proot/src/ptrace/ptrace.h b/app/src/main/cpp/proot/src/ptrace/ptrace.h new file mode 100644 index 0000000..9083ea9 --- /dev/null +++ b/app/src/main/cpp/proot/src/ptrace/ptrace.h @@ -0,0 +1,36 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef PTRACE_H +#define PTRACE_H + +#include "tracee/tracee.h" + +extern int translate_ptrace_enter(Tracee *tracee); +extern int translate_ptrace_exit(Tracee *tracee); +extern void attach_to_ptracer(Tracee *ptracee, Tracee *ptracer); +extern void detach_from_ptracer(Tracee *ptracee); + +#define PTRACEE (ptracee->as_ptracee) +#define PTRACER (ptracer->as_ptracer) + +#endif /* PTRACE_H */ diff --git a/app/src/main/cpp/proot/src/ptrace/wait.c b/app/src/main/cpp/proot/src/ptrace/wait.c new file mode 100644 index 0000000..af447d1 --- /dev/null +++ b/app/src/main/cpp/proot/src/ptrace/wait.c @@ -0,0 +1,393 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* PTRACE_*, */ +#include /* E*, */ +#include /* assert(3), */ +#include /* bool, true, false, */ +#include /* SIG*, */ +#include /* talloc*, */ + +#include "ptrace/wait.h" +#include "ptrace/ptrace.h" +#include "syscall/sysnum.h" +#include "syscall/chain.h" +#include "tracee/tracee.h" +#include "tracee/event.h" +#include "tracee/reg.h" +#include "tracee/mem.h" + +#include "attribute.h" + +static const char *stringify_event(int event) UNUSED; +static const char *stringify_event(int event) +{ + if (WIFEXITED(event)) + return "exited"; + else if (WIFSIGNALED(event)) + return "signaled"; + else if (WIFCONTINUED(event)) + return "continued"; + else if (WIFSTOPPED(event)) { + switch ((event & 0xfff00) >> 8) { + case SIGTRAP: + return "stopped: SIGTRAP"; + case SIGTRAP | 0x80: + return "stopped: SIGTRAP: 0x80"; + case SIGTRAP | PTRACE_EVENT_VFORK << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_VFORK"; + case SIGTRAP | PTRACE_EVENT_FORK << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_FORK"; + case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_VFORK_DONE"; + case SIGTRAP | PTRACE_EVENT_CLONE << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_CLONE"; + case SIGTRAP | PTRACE_EVENT_EXEC << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_EXEC"; + case SIGTRAP | PTRACE_EVENT_EXIT << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_EXIT"; + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_SECCOMP2"; + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: + return "stopped: SIGTRAP: PTRACE_EVENT_SECCOMP"; + case SIGSTOP: + return "stopped: SIGSTOP"; + default: + return "stopped: unknown"; + } + } + return "unknown"; +} + +/** + * Translate the wait syscall made by @ptracer into a "void" syscall + * if the expected pid is one of its ptracees, in order to emulate the + * ptrace mechanism within PRoot. This function returns -errno if an + * error occured (unsupported request), otherwise 0. + */ +int translate_wait_enter(Tracee *ptracer) +{ + Tracee *ptracee; + pid_t pid; + + PTRACER.waits_in = WAITS_IN_KERNEL; + + /* Don't emulate the ptrace mechanism if it's not a ptracer. */ + if (PTRACER.nb_ptracees == 0) + return 0; + + /* Don't emulate the ptrace mechanism if the requested pid is + * not a ptracee. */ + pid = (pid_t) peek_reg(ptracer, ORIGINAL, SYSARG_1); + if (pid != -1) { + ptracee = get_tracee(ptracer, pid, false); + if (ptracee == NULL || PTRACEE.ptracer != ptracer) + return 0; + } + + /* This syscall is canceled at the enter stage in order to be + * handled at the exit stage. */ + set_sysnum(ptracer, PR_void); + PTRACER.waits_in = WAITS_IN_PROOT; + + return 0; +} + +/** + * Update pid & wait status of @ptracer's wait(2) for the given + * @ptracee. This function returns -errno if an error occurred, 0 if + * the wait syscall will be restarted (ie. the event is discarded), + * otherwise @ptracee's pid. + */ +static int update_wait_status(Tracee *ptracer, Tracee *ptracee) +{ + word_t address; + int result; + + /* Special case: the Linux kernel reports the terminating + * event issued by a process to both its parent and its + * tracer, except when they are the same. In this case the + * Linux kernel reports the terminating event only once to the + * tracing parent ... */ + if (PTRACEE.ptracer == ptracee->parent + && (WIFEXITED(PTRACEE.event4.ptracer.value) + || WIFSIGNALED(PTRACEE.event4.ptracer.value))) { + /* ... So hide this terminating event (toward its + * tracer, ie. PRoot) and make the second one appear + * (towards its parent, ie. the ptracer). This will + * ensure its exit status is collected from a kernel + * point-of-view (ie. it doesn't stay a zombie + * forever). */ + restart_original_syscall(ptracer); + + /* Detach this ptracee from its ptracer, PRoot doesn't + * have anything else to emulate. */ + detach_from_ptracer(ptracee); + + /* Zombies can rest in peace once the ptracer is + * notified. */ + if (PTRACEE.is_zombie) + TALLOC_FREE(ptracee); + + return 0; + } + + address = peek_reg(ptracer, ORIGINAL, SYSARG_2); + if (address != 0) { + poke_int32(ptracer, address, PTRACEE.event4.ptracer.value); + if (errno != 0) + return -errno; + } + + PTRACEE.event4.ptracer.pending = false; + + /* Be careful; ptracee might get freed before its pid is + * returned. */ + result = ptracee->pid; + + /* Zombies can rest in peace once the ptracer is notified. */ + if (PTRACEE.is_zombie) { + detach_from_ptracer(ptracee); + TALLOC_FREE(ptracee); + } + + return result; +} + +/** + * Emulate the wait* syscall made by @ptracer if it was in the context + * of the ptrace mechanism. This function returns -errno if an error + * occured, otherwise the pid of the expected tracee. + */ +int translate_wait_exit(Tracee *ptracer) +{ + Tracee *ptracee; + word_t options; + int status; + pid_t pid; + + assert(PTRACER.waits_in == WAITS_IN_PROOT); + PTRACER.waits_in = DOESNT_WAIT; + + pid = (pid_t) peek_reg(ptracer, ORIGINAL, SYSARG_1); + options = peek_reg(ptracer, ORIGINAL, SYSARG_3); + + /* Is there such a stopped ptracee with an event not yet + * passed to its ptracer? */ + ptracee = get_stopped_ptracee(ptracer, pid, true, options); + if (ptracee == NULL) { + /* Is there still living ptracees? */ + if (PTRACER.nb_ptracees == 0) + return -ECHILD; + + /* Non blocking wait(2) ? */ + if ((options & WNOHANG) != 0) { + /* if WNOHANG was specified and one or more + * child(ren) specified by pid exist, but have + * not yet changed state, then 0 is returned. + * On error, -1 is returned. + * + * -- man 2 waitpid */ + return (has_ptracees(ptracer, pid, options) ? 0 : -ECHILD); + } + + /* Otherwise put this ptracer in the "waiting for + * ptracee" state, it will be woken up in + * handle_ptracee_event() later. */ + PTRACER.wait_pid = pid; + PTRACER.wait_options = options; + + return 0; + } + + status = update_wait_status(ptracer, ptracee); + if (status < 0) + return status; + + return status; +} + +/** + * For the given @ptracee, pass its current @event to its ptracer if + * this latter is waiting for it, otherwise put the @ptracee in the + * "waiting for ptracer" state. This function returns whether + * @ptracee shall be kept in the stop state or not. + */ +bool handle_ptracee_event(Tracee *ptracee, int event) +{ + bool handled_by_proot_first = false; + bool handled_by_proot_first_may_suppress = false; + Tracee *ptracer = PTRACEE.ptracer; + bool keep_stopped; + + assert(ptracer != NULL); + + /* Remember what the event initially was, this will be + * required by PRoot to handle this event later. */ + PTRACEE.event4.proot.value = event; + PTRACEE.event4.proot.pending = true; + + /* By default, this ptracee should be kept stopped until its + * ptracer restarts it. */ + keep_stopped = true; + + /* Not all events are expected for this ptracee. */ + if (WIFSTOPPED(event)) { + switch ((event & 0xfff00) >> 8) { + case SIGTRAP | 0x80: + if (PTRACEE.ignore_syscalls || PTRACEE.ignore_loader_syscalls) + return false; + + if ((PTRACEE.options & PTRACE_O_TRACESYSGOOD) == 0) + event &= ~(0x80 << 8); + + handled_by_proot_first = IS_IN_SYSEXIT(ptracee); + break; + +#define PTRACE_EVENT_VFORKDONE PTRACE_EVENT_VFORK_DONE +#define CASE_FILTER_EVENT(name) \ + case SIGTRAP | PTRACE_EVENT_ ##name << 8: \ + if ((PTRACEE.options & PTRACE_O_TRACE ##name) == 0) \ + return false; \ + PTRACEE.tracing_started = true; \ + handled_by_proot_first = true; \ + break; + + CASE_FILTER_EVENT(FORK); + CASE_FILTER_EVENT(VFORK); + CASE_FILTER_EVENT(VFORKDONE); + CASE_FILTER_EVENT(CLONE); + CASE_FILTER_EVENT(EXIT); + CASE_FILTER_EVENT(EXEC); + + /* Never reached. */ + assert(0); + + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: + /* These events are not supported [yet?] under + * ptrace emulation. */ + return false; + + case SIGSYS: + handled_by_proot_first = true; + handled_by_proot_first_may_suppress = true; + break; + + default: + PTRACEE.tracing_started = true; + break; + } + } + /* In these cases, the ptracee isn't really alive anymore. To + * ensure it will not be in limbo, PRoot restarts it whether + * its ptracer is waiting for it or not. */ + else if (WIFEXITED(event) || WIFSIGNALED(event)) { + PTRACEE.tracing_started = true; + keep_stopped = false; + } + + /* A process is not traced right from the TRACEME request; it + * is traced from the first received signal, whether it was + * raised by the process itself (implicitly or explicitly), or + * it was induced by a PTRACE_EVENT_*. */ + if (!PTRACEE.tracing_started) + return false; + + /* Under some circumstances, the event must be handled by + * PRoot first. */ + if (handled_by_proot_first) { + int signal; + signal = handle_tracee_event(ptracee, PTRACEE.event4.proot.value); + PTRACEE.event4.proot.value = signal; + + if (handled_by_proot_first_may_suppress) { + /* If we've decided to suppress signal + * (e.g. because seccomp policy blocked syscall + * but we emulate that syscall), + * don't notify ptracer and let ptracee resume. */ + if (signal == 0) { + if (seccomp_event_happens_after_enter_sigtrap()) { + if (PTRACEE.ignore_syscalls) + { + restart_tracee(ptracee, 0); + return true; + } + /* However if we've already notified ptracer about syscall entry + * before knowing it'll be blocked, notify ptracer about exit. */ + PTRACEE.event4.proot.value = 0; + if (PTRACEE.options & PTRACE_O_TRACESYSGOOD) { + event = (SIGTRAP | 0x80) << 8 | 0x7f; + } else { + event = SIGTRAP << 8 | 0x7f; + } + } else { + restart_tracee(ptracee, 0); + return true; + } + } + } else { + /* The computed signal is always 0 since we can come + * in this block only on sysexit and special events + * (as for now). */ + assert(signal == 0); + } + } + + /* Remember what the new event is, this will be required by + the ptracer in translate_ptrace_exit() in order to restart + this ptracee. */ + PTRACEE.event4.ptracer.value = event; + PTRACEE.event4.ptracer.pending = true; + + /* Notify asynchronously the ptracer about this event, as the + * kernel does. */ + kill(ptracer->pid, SIGCHLD); + + /* Note: wait_pid is set in translate_wait_exit() if no + * ptracee event was pending when the ptracer started to + * wait. */ + if ( (PTRACER.wait_pid == -1 || PTRACER.wait_pid == ptracee->pid) + && EXPECTED_WAIT_CLONE(PTRACER.wait_options, ptracee)) { + bool restarted; + int status; + + status = update_wait_status(ptracer, ptracee); + if (status == 0) + chain_next_syscall(ptracer); + else + poke_reg(ptracer, SYSARG_RESULT, (word_t) status); + + /* Write ptracer's register cache back. */ + (void) push_regs(ptracer); + + /* Restart the ptracer. */ + PTRACER.wait_pid = 0; + restarted = restart_tracee(ptracer, 0); + if (!restarted) + keep_stopped = false; + + return keep_stopped; + } + + return keep_stopped; +} diff --git a/app/src/main/cpp/proot/src/ptrace/wait.h b/app/src/main/cpp/proot/src/ptrace/wait.h new file mode 100644 index 0000000..8648f6f --- /dev/null +++ b/app/src/main/cpp/proot/src/ptrace/wait.h @@ -0,0 +1,49 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef PTRACE_WAIT_H +#define PTRACE_WAIT_H + +#include /* for __WALL */ + +#include "tracee/tracee.h" + +extern int translate_wait_enter(Tracee *ptracer); +extern int translate_wait_exit(Tracee *ptracer); +extern bool handle_ptracee_event(Tracee *ptracee, int wait_status); + +/* __WCLONE: Wait for "clone" children only. If omitted then wait for + * "non-clone" children only. (A "clone" child is one which delivers + * no signal, or a signal other than SIGCHLD to its parent upon + * termination.) This option is ignored if __WALL is also specified. + * + * __WALL: Wait for all children, regardless of type ("clone" or + * "non-clone"). + * + * -- wait(2) man-page + */ +#define EXPECTED_WAIT_CLONE(wait_options, tracee) \ + ((((wait_options) & __WALL) != 0) \ + || ((((wait_options) & __WCLONE) != 0) && (tracee)->clone) \ + || ((((wait_options) & __WCLONE) == 0) && !(tracee)->clone)) + +#endif /* PTRACE_WAIT_H */ diff --git a/app/src/main/cpp/proot/src/syscall/chain.c b/app/src/main/cpp/proot/src/syscall/chain.c new file mode 100644 index 0000000..93d8833 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/chain.c @@ -0,0 +1,185 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* talloc*, */ +#include /* STAILQ_*, */ +#include /* E*, */ +#include /* assert(3), */ + +#include "cli/note.h" +#include "syscall/chain.h" +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "arch.h" + +struct chained_syscall { + Sysnum sysnum; + word_t sysargs[6]; + STAILQ_ENTRY(chained_syscall) link; +}; + +STAILQ_HEAD(chained_syscalls, chained_syscall); + +static int register_chained_syscall_internal(Tracee *tracee, Sysnum sysnum, + word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6, + bool at_front) +{ + struct chained_syscall *syscall; + + if (tracee->chain.syscalls == NULL) { + tracee->chain.syscalls = talloc_zero(tracee, struct chained_syscalls); + if (tracee->chain.syscalls == NULL) + return -ENOMEM; + + STAILQ_INIT(tracee->chain.syscalls); + } + + syscall = talloc_zero(tracee->chain.syscalls, struct chained_syscall); + if (syscall == NULL) + return -ENOMEM; + + syscall->sysnum = sysnum; + syscall->sysargs[0] = sysarg_1; + syscall->sysargs[1] = sysarg_2; + syscall->sysargs[2] = sysarg_3; + syscall->sysargs[3] = sysarg_4; + syscall->sysargs[4] = sysarg_5; + syscall->sysargs[5] = sysarg_6; + + if (at_front) { + STAILQ_INSERT_HEAD(tracee->chain.syscalls, syscall, link); + } else { + STAILQ_INSERT_TAIL(tracee->chain.syscalls, syscall, link); + } + + return 0; +} + +/** + * Append a new syscall (@sysnum, @sysarg_*) to the list of + * "unrequested" syscalls for the given @tracee. These new syscalls + * will be triggered in order once the current syscall is done. The + * caller is free to force the last result of this syscall chain in + * @tracee->chain.final_result. This function returns -errno if an + * error occurred, otherwise 0. + */ +int register_chained_syscall(Tracee *tracee, Sysnum sysnum, + word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6) { + return register_chained_syscall_internal( + tracee, sysnum, + sysarg_1, sysarg_2, sysarg_3, + sysarg_4, sysarg_5, sysarg_6, + false + ); +} + +/** + * Use/remove the first element of @tracee->chain.syscalls to forge a + * new syscall. This function should be called only at the end of in + * the sysexit stage. + */ +void chain_next_syscall(Tracee *tracee) +{ + struct chained_syscall *syscall; + word_t instr_pointer; + word_t sysnum; + + assert(tracee->chain.syscalls != NULL); + + /* No more chained syscalls: force the result of the initial + * syscall (the one explicitly requested by the tracee). */ + if (STAILQ_EMPTY(tracee->chain.syscalls)) { + TALLOC_FREE(tracee->chain.syscalls); + + if (tracee->chain.force_final_result) + poke_reg(tracee, SYSARG_RESULT, tracee->chain.final_result); + + tracee->chain.force_final_result = false; + tracee->chain.final_result = 0; + + VERBOSE(tracee, 2, "chain_next_syscall finish"); + + return; + } + + VERBOSE(tracee, 2, "chain_next_syscall continue"); + + /* Original register values will be restored right after the + * last chained syscall. */ + tracee->restore_original_regs = false; + + /* The list of chained syscalls is a FIFO. */ + syscall = STAILQ_FIRST(tracee->chain.syscalls); + STAILQ_REMOVE_HEAD(tracee->chain.syscalls, link); + + poke_reg(tracee, SYSARG_1, syscall->sysargs[0]); + poke_reg(tracee, SYSARG_2, syscall->sysargs[1]); + poke_reg(tracee, SYSARG_3, syscall->sysargs[2]); + poke_reg(tracee, SYSARG_4, syscall->sysargs[3]); + poke_reg(tracee, SYSARG_5, syscall->sysargs[4]); + poke_reg(tracee, SYSARG_6, syscall->sysargs[5]); + + sysnum = detranslate_sysnum(get_abi(tracee), syscall->sysnum); + poke_reg(tracee, SYSTRAP_NUM, sysnum); + + /* Move the instruction pointer back to the original trap. */ + instr_pointer = peek_reg(tracee, CURRENT, INSTR_POINTER); + poke_reg(tracee, INSTR_POINTER, instr_pointer - get_systrap_size(tracee)); + + /* Break after exit from syscall, there may be another one in chain */ + tracee->restart_how = PTRACE_SYSCALL; +} + +/** + * Restart the original syscall of the given @tracee. The result of + * the current syscall will be overwritten. This function returns the + * same status as register_chained_syscall(). + */ +int restart_original_syscall(Tracee *tracee) +{ + return register_chained_syscall(tracee, + get_sysnum(tracee, ORIGINAL), + peek_reg(tracee, ORIGINAL, SYSARG_1), + peek_reg(tracee, ORIGINAL, SYSARG_2), + peek_reg(tracee, ORIGINAL, SYSARG_3), + peek_reg(tracee, ORIGINAL, SYSARG_4), + peek_reg(tracee, ORIGINAL, SYSARG_5), + peek_reg(tracee, ORIGINAL, SYSARG_6)); +} + +int restart_current_syscall_as_chained(Tracee *tracee) +{ + assert(tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_INACTIVE); + tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL; + return register_chained_syscall_internal(tracee, + get_sysnum(tracee, CURRENT), + peek_reg(tracee, CURRENT, SYSARG_1), + peek_reg(tracee, CURRENT, SYSARG_2), + peek_reg(tracee, CURRENT, SYSARG_3), + peek_reg(tracee, CURRENT, SYSARG_4), + peek_reg(tracee, CURRENT, SYSARG_5), + peek_reg(tracee, CURRENT, SYSARG_6), + true); +} diff --git a/app/src/main/cpp/proot/src/syscall/chain.h b/app/src/main/cpp/proot/src/syscall/chain.h new file mode 100644 index 0000000..ed3e421 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/chain.h @@ -0,0 +1,41 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef CHAIN_H +#define CHAIN_H + +#include "tracee/tracee.h" +#include "syscall/sysnum.h" +#include "arch.h" + +extern int register_chained_syscall(Tracee *tracee, Sysnum sysnum, + word_t sysarg_1, word_t sysarg_2, word_t sysarg_3, + word_t sysarg_4, word_t sysarg_5, word_t sysarg_6); + +extern int restart_original_syscall(Tracee *tracee); + +extern void chain_next_syscall(Tracee *tracee); + +extern int restart_current_syscall_as_chained(Tracee *tracee); + + +#endif /* CHAIN_H */ diff --git a/app/src/main/cpp/proot/src/syscall/enter.c b/app/src/main/cpp/proot/src/syscall/enter.c new file mode 100644 index 0000000..7b33280 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/enter.c @@ -0,0 +1,466 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* errno(3), E* */ +#include /* talloc_*, */ +#include /* struct sockaddr_un, */ +#include /* SYS_*, */ +#include /* AT_FDCWD, */ +#include /* PATH_MAX, */ +#include /* strcpy */ +#include /* PR_SET_DUMPABLE */ +#include /* TCSETS, TCSANOW */ + +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/socket.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "syscall/heap.h" +#include "execve/execve.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "path/path.h" +#include "path/canon.h" +#include "arch.h" + +/** + * Translate @path and put the result in the @tracee's memory address + * space pointed to by the @reg argument of the current syscall. See + * the documentation of translate_path() about the meaning of + * @type. This function returns -errno if an error occured, otherwise + * 0. + */ +static int translate_path2(Tracee *tracee, int dir_fd, char path[PATH_MAX], Reg reg, Type type) +{ + char new_path[PATH_MAX]; + int status; + + /* Special case where the argument was NULL. */ + if (path[0] == '\0') + return 0; + + /* Translate the original path. */ + status = translate_path(tracee, new_path, dir_fd, path, type != SYMLINK); + if (status < 0) + return status; + + return set_sysarg_path(tracee, new_path, reg); +} + +/** + * A helper, see the comment of the function above. + */ +static int translate_sysarg(Tracee *tracee, Reg reg, Type type) +{ + char old_path[PATH_MAX]; + int status; + + /* Extract the original path. */ + status = get_sysarg_path(tracee, old_path, reg); + if (status < 0) + return status; + + return translate_path2(tracee, AT_FDCWD, old_path, reg, type); +} + +/** + * Translate the input arguments of the current @tracee's syscall in the + * @tracee->pid process area. This function sets @tracee->status to + * -errno if an error occured from the tracee's point-of-view (EFAULT + * for instance), otherwise 0. + */ +int translate_syscall_enter(Tracee *tracee) +{ + int flags; + int dirfd; + int olddirfd; + int newdirfd; + + int status = 0; + + char path[PATH_MAX]; + char oldpath[PATH_MAX]; + char newpath[PATH_MAX]; + + word_t syscall_number; + bool special = false; + + /* Translate input arguments. */ + syscall_number = get_sysnum(tracee, ORIGINAL); + switch (syscall_number) { + case PR_execve: + status = translate_execve_enter(tracee); + break; + + case PR_ptrace: + status = translate_ptrace_enter(tracee); + break; + + case PR_wait4: + case PR_waitpid: + status = translate_wait_enter(tracee); + break; + + case PR_brk: + translate_brk_enter(tracee); + status = 0; + break; + + case PR_getcwd: + set_sysnum(tracee, PR_void); + status = 0; + break; + + case PR_fchdir: + case PR_chdir: { + struct stat statl; + char *tmp; + + /* The ending "." ensures an error will be reported if + * path does not exist or if it is not a directory. */ + if (syscall_number == PR_chdir) { + status = get_sysarg_path(tracee, path, SYSARG_1); + if (status < 0) + break; + + join_paths(oldpath, path, "."); + dirfd = AT_FDCWD; + } + else { + strcpy(oldpath, "."); + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + } + + status = translate_path(tracee, path, dirfd, oldpath, true); + if (status < 0) + break; + + status = lstat(path, &statl); + if (status < 0) + break; + + /* Check this directory is accessible. */ + if ((statl.st_mode & S_IXUSR) == 0) + return -EACCES; + + /* Sadly this method doesn't detranslate statefully, + * this means that there's an ambiguity when several + * bindings are from the same host path: + * + * $ proot -m /tmp:/a -m /tmp:/b fchdir_getcwd /a + * /b + * + * $ proot -m /tmp:/b -m /tmp:/a fchdir_getcwd /a + * /a + * + * A solution would be to follow each file descriptor + * just like it is done for cwd. + */ + + status = detranslate_path(tracee, path, NULL); + if (status < 0) + break; + + /* Remove the trailing "/" or "/.". */ + chop_finality(path); + + tmp = talloc_strdup(tracee->fs, path); + if (tmp == NULL) { + status = -ENOMEM; + break; + } + TALLOC_FREE(tracee->fs->cwd); + + tracee->fs->cwd = tmp; + talloc_set_name_const(tracee->fs->cwd, "$cwd"); + + set_sysnum(tracee, PR_void); + status = 0; + break; + } + + case PR_bind: + case PR_connect: { + word_t address; + word_t size; + + address = peek_reg(tracee, CURRENT, SYSARG_2); + size = peek_reg(tracee, CURRENT, SYSARG_3); + + status = translate_socketcall_enter(tracee, &address, size); + if (status <= 0) + break; + + poke_reg(tracee, SYSARG_2, address); + poke_reg(tracee, SYSARG_3, sizeof(struct sockaddr_un)); + + status = 0; + break; + } + +#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) + +#define PEEK_WORD(addr, forced_errno) \ + peek_word(tracee, addr); \ + if (errno != 0) { \ + status = forced_errno ?: -errno; \ + break; \ + } + +#define POKE_WORD(addr, value) \ + poke_word(tracee, addr, value); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + + case PR_accept: + case PR_accept4: + /* Nothing special to do if no sockaddr was specified. */ + if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) { + status = 0; + break; + } + special = true; + /* Fall through. */ + case PR_getsockname: + case PR_getpeername:{ + int size; + + /* Remember: PEEK_WORD puts -errno in status and breaks if an + * error occured. */ + size = (int) PEEK_WORD(peek_reg(tracee, ORIGINAL, SYSARG_3), special ? -EINVAL : 0); + + /* The "size" argument is both used as an input parameter + * (max. size) and as an output parameter (actual size). The + * exit stage needs to know the max. size to not overwrite + * anything, that's why it is copied in the 6th argument + * (unused) before the kernel updates it. */ + poke_reg(tracee, SYSARG_6, size); + + status = 0; + break; + } + +#undef SYSARG_ADDR +#undef PEEK_WORD +#undef POKE_WORD + + case PR_access: + case PR_chmod: + case PR_chown: + case PR_chown32: + case PR_getxattr: + case PR_listxattr: + case PR_mknod: + case PR_removexattr: + case PR_setxattr: + case PR_stat: + case PR_stat64: + case PR_statfs: + case PR_statfs64: + case PR_truncate: + case PR_truncate64: + case PR_utime: + case PR_utimes: + status = translate_sysarg(tracee, SYSARG_1, REGULAR); + break; + + case PR_open: + flags = peek_reg(tracee, CURRENT, SYSARG_2); + + if ( ((flags & O_NOFOLLOW) != 0) + || ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)) + status = translate_sysarg(tracee, SYSARG_1, SYMLINK); + else + status = translate_sysarg(tracee, SYSARG_1, REGULAR); + break; + + case PR_fchownat: + case PR_fstatat64: + case PR_newfstatat: + case PR_utimensat: + case PR_name_to_handle_at: + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + + status = get_sysarg_path(tracee, path, SYSARG_2); + if (status < 0) + break; + + flags = ( syscall_number == PR_fchownat + || syscall_number == PR_name_to_handle_at) + ? peek_reg(tracee, CURRENT, SYSARG_5) + : peek_reg(tracee, CURRENT, SYSARG_4); + + if ((flags & AT_SYMLINK_NOFOLLOW) != 0) + status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); + else + status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); + break; + + case PR_fchmodat: + case PR_faccessat: + case PR_faccessat2: + case PR_futimesat: + case PR_mknodat: + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + + status = get_sysarg_path(tracee, path, SYSARG_2); + if (status < 0) + break; + + status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); + break; + + case PR_inotify_add_watch: + flags = peek_reg(tracee, CURRENT, SYSARG_3); + + if ((flags & IN_DONT_FOLLOW) != 0) + status = translate_sysarg(tracee, SYSARG_2, SYMLINK); + else + status = translate_sysarg(tracee, SYSARG_2, REGULAR); + break; + + case PR_readlink: + case PR_lchown: + case PR_lchown32: + case PR_lgetxattr: + case PR_llistxattr: + case PR_lremovexattr: + case PR_lsetxattr: + case PR_lstat: + case PR_lstat64: + case PR_unlink: + case PR_rmdir: + case PR_mkdir: + status = translate_sysarg(tracee, SYSARG_1, SYMLINK); + break; + + case PR_linkat: + olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); + newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); + flags = peek_reg(tracee, CURRENT, SYSARG_5); + + status = get_sysarg_path(tracee, oldpath, SYSARG_2); + if (status < 0) + break; + + status = get_sysarg_path(tracee, newpath, SYSARG_4); + if (status < 0) + break; + + if ((flags & AT_SYMLINK_FOLLOW) != 0) + status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, REGULAR); + else + status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK); + if (status < 0) + break; + + status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK); + break; + + case PR_openat: + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + flags = peek_reg(tracee, CURRENT, SYSARG_3); + + status = get_sysarg_path(tracee, path, SYSARG_2); + if (status < 0) + break; + + if ( ((flags & O_NOFOLLOW) != 0) + || ((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)) + status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); + else + status = translate_path2(tracee, dirfd, path, SYSARG_2, REGULAR); + break; + + case PR_readlinkat: + case PR_unlinkat: + case PR_mkdirat: + dirfd = peek_reg(tracee, CURRENT, SYSARG_1); + + status = get_sysarg_path(tracee, path, SYSARG_2); + if (status < 0) + break; + + status = translate_path2(tracee, dirfd, path, SYSARG_2, SYMLINK); + break; + + case PR_link: + case PR_rename: + status = translate_sysarg(tracee, SYSARG_1, SYMLINK); + if (status < 0) + break; + + status = translate_sysarg(tracee, SYSARG_2, SYMLINK); + break; + + case PR_renameat: + case PR_renameat2: + olddirfd = peek_reg(tracee, CURRENT, SYSARG_1); + newdirfd = peek_reg(tracee, CURRENT, SYSARG_3); + + status = get_sysarg_path(tracee, oldpath, SYSARG_2); + if (status < 0) + break; + + status = get_sysarg_path(tracee, newpath, SYSARG_4); + if (status < 0) + break; + + status = translate_path2(tracee, olddirfd, oldpath, SYSARG_2, SYMLINK); + if (status < 0) + break; + + status = translate_path2(tracee, newdirfd, newpath, SYSARG_4, SYMLINK); + break; + + case PR_symlink: + status = translate_sysarg(tracee, SYSARG_2, SYMLINK); + break; + + case PR_symlinkat: + newdirfd = peek_reg(tracee, CURRENT, SYSARG_2); + + status = get_sysarg_path(tracee, newpath, SYSARG_3); + if (status < 0) + break; + + status = translate_path2(tracee, newdirfd, newpath, SYSARG_3, SYMLINK); + break; + + case PR_prctl: + /* Prevent tracees from setting dumpable flag. + * (Otherwise it could break tracee memory access) */ + if (peek_reg(tracee, CURRENT, SYSARG_1) == PR_SET_DUMPABLE) { + set_sysnum(tracee, PR_void); + status = 0; + } + break; + } + + return status; +} + diff --git a/app/src/main/cpp/proot/src/syscall/exit.c b/app/src/main/cpp/proot/src/syscall/exit.c new file mode 100644 index 0000000..36448d2 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/exit.c @@ -0,0 +1,435 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* errno(3), E* */ +#include /* struct utsname, */ +#include /* SYS_*, */ +#include /* strlen(3), */ + +#include "cli/note.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "syscall/socket.h" +#include "syscall/chain.h" +#include "syscall/heap.h" +#include "syscall/rlimit.h" +#include "execve/execve.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "tracee/seccomp.h" +#include "path/path.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "arch.h" + +/** + * Translate the output arguments of the current @tracee's syscall in + * the @tracee->pid process area. This function sets the result of + * this syscall to @tracee->status if an error occured previously + * during the translation, that is, if @tracee->status is less than 0. + */ +void translate_syscall_exit(Tracee *tracee) +{ + word_t syscall_number; + word_t syscall_result; + int status = 0; + + /* Set the tracee's errno if an error occured previously during + * the translation. */ + if (tracee->status < 0) { + poke_reg(tracee, SYSARG_RESULT, (word_t) tracee->status); + return; + } + + /* If proot changed syscall to PR_void during enter, + * keep syscall result set during entry. */ + if (peek_reg(tracee, MODIFIED, SYSARG_NUM) == SYSCALL_AVOIDER && + peek_reg(tracee, ORIGINAL, SYSARG_NUM) != SYSCALL_AVOIDER) { + poke_reg(tracee, SYSARG_RESULT, peek_reg(tracee, MODIFIED, SYSARG_RESULT)); + } + + /* Translate output arguments: + * - break: update the syscall result register with "status" + * - return: nothing else to do. + */ + syscall_number = get_sysnum(tracee, ORIGINAL); + syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + switch (syscall_number) { + case PR_brk: + translate_brk_exit(tracee); + return; + + case PR_getcwd: { + char path[PATH_MAX]; + size_t new_size; + size_t size; + word_t output; + + size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2); + if (size == 0) { + status = -EINVAL; + break; + } + + /* Ensure cwd still exists. */ + status = translate_path(tracee, path, AT_FDCWD, ".", false); + if (status < 0) + break; + + new_size = strlen(tracee->fs->cwd) + 1; + if (size < new_size) { + status = -ERANGE; + break; + } + + /* Overwrite the path. */ + output = peek_reg(tracee, ORIGINAL, SYSARG_1); + status = write_data(tracee, output, tracee->fs->cwd, new_size); + if (status < 0) + break; + + /* The value of "status" is used to update the returned value + * in translate_syscall_exit(). */ + status = new_size; + break; + } + + case PR_accept: + case PR_accept4: + /* Nothing special to do if no sockaddr was specified. */ + if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0) + return; + /* Fall through. */ + case PR_getsockname: + case PR_getpeername: { + word_t sock_addr; + word_t size_addr; + word_t max_size; + + /* Error reported by the kernel. */ + if ((int) syscall_result < 0) + return; + + sock_addr = peek_reg(tracee, ORIGINAL, SYSARG_2); + size_addr = peek_reg(tracee, MODIFIED, SYSARG_3); + max_size = peek_reg(tracee, MODIFIED, SYSARG_6); + + status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size); + if (status < 0) + break; + + /* Don't overwrite the syscall result. */ + return; + } + +#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee)) + +#define POKE_WORD(addr, value) \ + poke_word(tracee, addr, value); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + +#define PEEK_WORD(addr) \ + peek_word(tracee, addr); \ + if (errno != 0) { \ + status = -errno; \ + break; \ + } + +#undef SYSARG_ADDR +#undef PEEK_WORD +#undef POKE_WORD + + case PR_fchdir: + case PR_chdir: + /* These syscalls are fully emulated, see enter.c for details + * (like errors). */ + status = 0; + break; + + case PR_rename: + case PR_renameat: { + char old_path[PATH_MAX]; + char new_path[PATH_MAX]; + ssize_t old_length; + ssize_t new_length; + Comparison comparison; + Reg old_reg; + Reg new_reg; + char *tmp; + + /* Error reported by the kernel. */ + if ((int) syscall_result < 0) + return; + + if (syscall_number == PR_rename) { + old_reg = SYSARG_1; + new_reg = SYSARG_2; + } + else { + old_reg = SYSARG_2; + new_reg = SYSARG_4; + } + + /* Get the old path, then convert it to the same + * "point-of-view" as tracee->fs->cwd (guest). */ + status = read_path(tracee, old_path, peek_reg(tracee, MODIFIED, old_reg)); + if (status < 0) + break; + + status = detranslate_path(tracee, old_path, NULL); + if (status < 0) + break; + old_length = (status > 0 ? status - 1 : (ssize_t) strlen(old_path)); + + /* Nothing special to do if the moved path is not the + * current working directory. */ + comparison = compare_paths(old_path, tracee->fs->cwd); + if (comparison != PATH1_IS_PREFIX && comparison != PATHS_ARE_EQUAL) { + status = 0; + break; + } + + /* Get the new path, then convert it to the same + * "point-of-view" as tracee->fs->cwd (guest). */ + status = read_path(tracee, new_path, peek_reg(tracee, MODIFIED, new_reg)); + if (status < 0) + break; + + status = detranslate_path(tracee, new_path, NULL); + if (status < 0) + break; + new_length = (status > 0 ? status - 1 : (ssize_t) strlen(new_path)); + + /* Sanity check. */ + if (strlen(tracee->fs->cwd) >= PATH_MAX) { + status = 0; + break; + } + strcpy(old_path, tracee->fs->cwd); + + /* Update the virtual current working directory. */ + substitute_path_prefix(old_path, old_length, new_path, new_length); + + tmp = talloc_strdup(tracee->fs, old_path); + if (tmp == NULL) { + status = -ENOMEM; + break; + } + + TALLOC_FREE(tracee->fs->cwd); + tracee->fs->cwd = tmp; + + status = 0; + break; + } + + case PR_readlink: + case PR_readlinkat: { + char referee[PATH_MAX]; + char referer[PATH_MAX]; + size_t old_size; + size_t new_size; + size_t max_size; + word_t input; + word_t output; + + /* Error reported by the kernel. */ + if ((int) syscall_result < 0) + return; + + old_size = syscall_result; + + if (syscall_number == PR_readlink) { + output = peek_reg(tracee, ORIGINAL, SYSARG_2); + max_size = peek_reg(tracee, ORIGINAL, SYSARG_3); + input = peek_reg(tracee, MODIFIED, SYSARG_1); + } + else { + output = peek_reg(tracee, ORIGINAL, SYSARG_3); + max_size = peek_reg(tracee, ORIGINAL, SYSARG_4); + input = peek_reg(tracee, MODIFIED, SYSARG_2); + } + + if (max_size > PATH_MAX) + max_size = PATH_MAX; + + if (max_size == 0) { + status = -EINVAL; + break; + } + + /* The kernel does NOT put the NULL terminating byte for + * readlink(2). */ + status = read_data(tracee, referee, output, old_size); + if (status < 0) + break; + referee[old_size] = '\0'; + + /* Not optimal but safe (path is fully translated). */ + status = read_path(tracee, referer, input); + if (status < 0) + break; + + if (status >= PATH_MAX) { + status = -ENAMETOOLONG; + break; + } + + if (status == 1) { + /* Empty path was passed (""), + * indicating that path is pointed to by fd passed in first argument */ + word_t dirfd = peek_reg(tracee, ORIGINAL, SYSARG_1); + if (syscall_number == PR_readlink || dirfd < 0) { + status = -EBADF; + break; + } + status = readlink_proc_pid_fd(tracee->pid, dirfd, referer); + if (status < 0) + break; + } + + status = detranslate_path(tracee, referee, referer); + if (status < 0) + break; + + /* The original path doesn't require any transformation, i.e + * it is a symetric binding. */ + if (status == 0) + return; + + /* Overwrite the path. Note: the output buffer might be + * initialized with zeros but it was updated with the kernel + * result, and then with the detranslated result. This later + * might be shorter than the former, so it's safier to add a + * NULL terminating byte when possible. This problem was + * exposed by IDA Demo 6.3. */ + if ((size_t) status < max_size) { + new_size = status - 1; + status = write_data(tracee, output, referee, status); + } + else { + new_size = max_size; + status = write_data(tracee, output, referee, max_size); + } + if (status < 0) + break; + + /* The value of "status" is used to update the returned value + * in translate_syscall_exit(). */ + status = new_size; + break; + } + + case PR_execve: + translate_execve_exit(tracee); + return; + + case PR_ptrace: + status = translate_ptrace_exit(tracee); + break; + + case PR_wait4: + case PR_waitpid: + if (tracee->as_ptracer.waits_in != WAITS_IN_PROOT) + return; + + status = translate_wait_exit(tracee); + break; + + case PR_setrlimit: + case PR_prlimit64: + /* Error reported by the kernel. */ + if ((int) syscall_result < 0) + return; + + status = translate_setrlimit_exit(tracee, syscall_number == PR_prlimit64); + if (status < 0) + break; + + /* Don't overwrite the syscall result. */ + return; + + case PR_utime: + if ((int) syscall_result == -ENOSYS) + { + fix_and_restart_enosys_syscall(tracee); + } + return; + + case PR_statfs: + case PR_statfs64: { + /* Possibly fake that /dev/shm is living on tmpfs */ + char devshm_path[PATH_MAX]; + char statfs_path[PATH_MAX]; + + /* Only perform changes to result of successful syscall + * (that is, path was valid, it doesn't have to point + * to mount root) */ + if (syscall_result != 0) { + return; + } + + if (translate_path(tracee, devshm_path, AT_FDCWD, "/dev/shm", true) < 0) { + VERBOSE(tracee, 5, "/dev/shm is not mounted, not changing statfs() result"); + return; + } + + if (read_path(tracee, statfs_path, peek_reg(tracee, MODIFIED, SYSARG_1)) < 0) { + VERBOSE(tracee, 5, "statfs() exit couldn't read statfs_path"); + return; + } + + Comparison comparison = compare_paths(devshm_path, statfs_path); + if (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX) { + VERBOSE(tracee, 5, "Updating statfs() result to fake tmpfs /dev/shm"); + /* Write TMPFS_MAGIC at beginning of statfs structure. + * + * (It's at beginning of structure regardless of syscall variant + * (statfs vs statfs64) and architecture bitness + * (on 64 bit this field is 8 bytes long, but as long as it's + * little endian, it will need only first 4 bytes to be modified, + * as next 4 bytes will always be 0)) + * */ + word_t stat_addr = peek_reg(tracee, ORIGINAL, syscall_number == PR_statfs64 ? SYSARG_3 : SYSARG_2); + int write_status = write_data(tracee, stat_addr, "\x94\x19\x02\x01", 4); + if (write_status < 0) { + VERBOSE(tracee, 5, "Updating statfs() result failed"); + } + } + else { + VERBOSE(tracee, 5, "statfs() not for /dev/shm, not changing result"); + } + + return; + } + + default: + return; + } + + poke_reg(tracee, SYSARG_RESULT, (word_t) status); +} diff --git a/app/src/main/cpp/proot/src/syscall/heap.c b/app/src/main/cpp/proot/src/syscall/heap.c new file mode 100644 index 0000000..29bd9d1 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/heap.c @@ -0,0 +1,207 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* PROT_*, MAP_*, */ +#include /* assert(3), */ +#include /* strerror(3), */ +#include /* sysconf(3), */ +#include /* MIN(), MAX(), */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "syscall/sysnum.h" +#include "execve/execve.h" +#include "cli/note.h" + +#include "compat.h" + +/* The size of the heap can be zero, unlike the size of a memory + * mapping. As a consequence, the first page of the "heap" memory + * mapping is discarded in order to emulate an empty heap. */ +static word_t heap_offset = 0; + +/** + * Put @tracee's heap to a reliable location. By default the Linux + * kernel puts it near loader's BSS, but this default location is not + * reliable since the kernel might put another memory mapping right + * after it (ie. continuously). In this case, @tracee's heap can't + * grow anymore and some programs like Bash will abort. This issue + * can be reproduced when using a Ubuntu 12.04 x86_64 rootfs on RHEL 5 + * x86_64. + */ +void translate_brk_enter(Tracee *tracee) +{ + word_t new_brk_address; + size_t old_heap_size; + size_t new_heap_size; + + if (tracee->heap->disabled) + return; + + if (heap_offset == 0) { + heap_offset = sysconf(_SC_PAGE_SIZE); + if ((int) heap_offset <= 0) + heap_offset = 0x1000; + } + + new_brk_address = peek_reg(tracee, CURRENT, SYSARG_1); + + /* Allocate a new mapping for the emulated heap. */ + if (tracee->heap->base == 0) { + Sysnum sysnum; + Mapping *mappings; + Mapping *bss; + + /* From PRoot's point-of-view this is the first time this + * tracee calls brk(2), although an address was specified. + * This is not supposed to happen the first time. It is + * likely because this tracee is the very first child of PRoot + * but the first execve(2) didn't happen yet (so this is not + * its first call to brk(2)). For instance, the installation + * of seccomp filters is made after this very first process is + * traced, and might call malloc(3) before the first + * execve(2). */ + if (new_brk_address != 0) { + if (tracee->verbose > 0) + note(tracee, WARNING, INTERNAL, + "process %d is doing suspicious brk()", tracee->pid); + return; + } + + /* Put the heap as close to the BSS as possible since + * some programs -- like dump-emacs -- assume the gap + * between the end of the BSS and the start of the + * heap is relatively small (ie. < 1MB) even if ALSR + * is enabled. Note that bss->addr + bss->length is + * naturally aligned to a page boundary according to + * add_mapping() in execve/enter.c, ie. no need to + * align new_brk_address again. Now, the gap between + * the BSS and the heap is only "heap_offset" bytes + * long. To emulate ADDR_NO_RANDOMIZE personality, + * this gap should be removed (not yet supported). */ + mappings = tracee->load_info->mappings; + bss = &mappings[talloc_array_length(mappings) - 1]; + new_brk_address = bss->addr + bss->length; + +#ifdef ARCH_ARM64 + sysnum = tracee->is_aarch32 ? PR_mmap2 : PR_mmap; +#else + sysnum = PR_mmap2; +#endif + + set_sysnum(tracee, sysnum); + poke_reg(tracee, SYSARG_1 /* address */, new_brk_address); + poke_reg(tracee, SYSARG_2 /* length */, heap_offset); + poke_reg(tracee, SYSARG_3 /* prot */, PROT_READ | PROT_WRITE); + poke_reg(tracee, SYSARG_4 /* flags */, MAP_PRIVATE | MAP_ANONYMOUS); + poke_reg(tracee, SYSARG_5 /* fd */, -1); + poke_reg(tracee, SYSARG_6 /* offset */, 0); + + return; + } + + /* The size of the heap can't be negative. */ + if (new_brk_address < tracee->heap->base) { + set_sysnum(tracee, PR_void); + return; + } + + new_heap_size = new_brk_address - tracee->heap->base; + old_heap_size = tracee->heap->size; + + /* Actually resizing. */ + set_sysnum(tracee, PR_mremap); + poke_reg(tracee, SYSARG_1 /* old_address */, tracee->heap->base - heap_offset); + poke_reg(tracee, SYSARG_2 /* old_size */, old_heap_size + heap_offset); + poke_reg(tracee, SYSARG_3 /* new_size */, new_heap_size + heap_offset); + poke_reg(tracee, SYSARG_4 /* flags */, 0); + poke_reg(tracee, SYSARG_5 /* new_address */, 0); + + return; +} + +/** + * c.f. function above. + */ +void translate_brk_exit(Tracee *tracee) +{ + word_t result; + word_t sysnum; + int tracee_errno; + + if (tracee->heap->disabled) + return; + + assert(heap_offset > 0); + + sysnum = get_sysnum(tracee, MODIFIED); + result = peek_reg(tracee, CURRENT, SYSARG_RESULT); + tracee_errno = (int) result; + + switch (sysnum) { + case PR_void: + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + + case PR_mmap: + case PR_mmap2: + /* On error, mmap(2) returns -errno (the last 4k is + * reserved for this), whereas brk(2) returns the + * previous value. */ + if (tracee_errno < 0 && tracee_errno > -4096) { + poke_reg(tracee, SYSARG_RESULT, 0); + break; + } + + tracee->heap->base = result + heap_offset; + tracee->heap->size = 0; + + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + + case PR_mremap: + /* On error, mremap(2) returns -errno (the last 4k is + * reserved this), whereas brk(2) returns the previous + * value. */ + if ( (tracee_errno < 0 && tracee_errno > -4096) + || (tracee->heap->base != result + heap_offset)) { + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + } + + tracee->heap->size = peek_reg(tracee, MODIFIED, SYSARG_3) - heap_offset; + + poke_reg(tracee, SYSARG_RESULT, tracee->heap->base + tracee->heap->size); + break; + + case PR_brk: + /* Is it confirmed that this suspicious call to brk(2) + * is actually legit? */ + if (result == peek_reg(tracee, ORIGINAL, SYSARG_1)) + tracee->heap->disabled = true; + break; + + default: + assert(0); + } +} diff --git a/app/src/main/cpp/proot/src/syscall/heap.h b/app/src/main/cpp/proot/src/syscall/heap.h new file mode 100644 index 0000000..8097834 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/heap.h @@ -0,0 +1,31 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef HEAP_H +#define HEAP_H + +#include "tracee/tracee.h" + +extern void translate_brk_enter(Tracee *tracee); +extern void translate_brk_exit(Tracee *tracee); + +#endif /* HEAP_H */ diff --git a/app/src/main/cpp/proot/src/syscall/rlimit.c b/app/src/main/cpp/proot/src/syscall/rlimit.c new file mode 100644 index 0000000..d167843 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/rlimit.c @@ -0,0 +1,117 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* bool, */ +#include /* prlimit(2), */ +#include /* prlimit(2), */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "cli/note.h" + +/** + * Set PRoot's stack soft limit to @tracee's one if this latter is + * greater. This allows to workaround a Linux kernel bug that + * prevents a tracer to access a tracee's stack beyond its last mapped + * page, as it might by the case under PRoot. This function returns + * -errno if an error occurred, otherwise 0. + * + * Details: when a tracer tries to access a tracee's stack beyond its + * last mapped page, the Linux kernel should be able to increase + * tracee's stack up to its soft limit. Unfortunately the Linux + * kernel checks the limit of the tracer instead the limit of the + * tracee. This bug was exposed using UMEQ under PRoot. + * + * Ref.: https://bugzilla.kernel.org/show_bug.cgi?id=91791 + * + * Three strategies were possible: + * + * - set PRoot's stack soft limit to the hard limit; this might make + * the system collapse if PRoot starts to recurses indefinitely. + * + * - as it's done here; this appears to be a good compromise between + * the strategy above and the one below. + * + * - as it's done here + reduce PRoot's stack soft limit as soon as + * it's possible; this would be overly complicated. + */ +int translate_setrlimit_exit(const Tracee *tracee, bool is_prlimit) +{ + struct rlimit64 proot_stack; + word_t resource; + word_t address; + word_t tracee_stack_limit; + Reg sysarg; + int status; + + sysarg = (is_prlimit ? SYSARG_2 : SYSARG_1); + + resource = peek_reg(tracee, ORIGINAL, sysarg); + address = peek_reg(tracee, ORIGINAL, sysarg + 1); + + /* Not the resource we're looking for? */ + if (resource != RLIMIT_STACK) + return 0; + + /* Retrieve new tracee's stack limit. */ + if (is_prlimit) { + /* Not the prlimit usage we're looking for? */ + if (address == 0) + return 0; + + tracee_stack_limit = peek_uint64(tracee, address); + } + else { + tracee_stack_limit = peek_word(tracee, address); + + /* Convert this special value from 32-bit to 64-bit, + * if needed. */ + if (is_32on64_mode(tracee) && tracee_stack_limit == (uint32_t) -1) + tracee_stack_limit = RLIM_INFINITY; + } + if (errno != 0) + return -errno; + + /* Get current PRoot's stack limit. */ + status = prlimit64(0, RLIMIT_STACK, NULL, &proot_stack); + if (status < 0) { + VERBOSE(tracee, 1, "can't get stack limit."); + return 0; /* Not fatal. */ + } + + /* No need to increase current PRoot's stack limit? */ + if (proot_stack.rlim_cur >= tracee_stack_limit) + return 0; + + proot_stack.rlim_cur = tracee_stack_limit; + + /* Increase current PRoot's stack limit. */ + status = prlimit64(0, RLIMIT_STACK, &proot_stack, NULL); + if (status < 0) + VERBOSE(tracee, 1, "can't set stack limit."); + return 0; /* Not fatal. */ + + VERBOSE(tracee, 1, "stack soft limit increased to %llu bytes", proot_stack.rlim_cur); + return 0; +} diff --git a/app/src/main/cpp/proot/src/syscall/rlimit.h b/app/src/main/cpp/proot/src/syscall/rlimit.h new file mode 100644 index 0000000..7792023 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/rlimit.h @@ -0,0 +1,31 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef RLIMIT_H +#define RLIMIT_H + +#include +#include "tracee/tracee.h" + +extern int translate_setrlimit_exit(const Tracee *tracee, bool is_prlimit); + +#endif /* RLIMIT_H */ diff --git a/app/src/main/cpp/proot/src/syscall/seccomp.c b/app/src/main/cpp/proot/src/syscall/seccomp.c new file mode 100644 index 0000000..ecfdd94 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/seccomp.c @@ -0,0 +1,471 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include "arch.h" + +#include /* prctl(2), PR_* */ +#include /* struct sock_*, */ +#include /* SECCOMP_MODE_FILTER, */ +#include /* struct sock_*, */ +#include /* AUDIT_, */ +#include /* LIST_FOREACH, */ +#include /* size_t, */ +#include /* talloc_*, */ +#include /* E*, */ +#include /* memcpy(3), */ +#include /* offsetof(3), */ +#include /* uint*_t, UINT*_MAX, */ +#include /* assert(3), */ + +#include "syscall/seccomp.h" +#include "tracee/tracee.h" +#include "syscall/syscall.h" +#include "syscall/sysnum.h" +#include "cli/note.h" + +#include "compat.h" +#include "attribute.h" + +#define DEBUG_FILTER(...) /* fprintf(stderr, __VA_ARGS__) */ + +/** + * Allocate an empty @program->filter. This function returns -errno + * if an error occurred, otherwise 0. + */ +static int new_program_filter(struct sock_fprog *program) +{ + program->filter = talloc_array(NULL, struct sock_filter, 0); + if (program->filter == NULL) + return -ENOMEM; + + program->len = 0; + return 0; +} + +/** + * Append to @program->filter the given @statements (@nb_statements + * items). This function returns -errno if an error occurred, + * otherwise 0. + */ +static int add_statements(struct sock_fprog *program, size_t nb_statements, + struct sock_filter *statements) +{ + size_t length; + void *tmp; + size_t i; + + length = talloc_array_length(program->filter); + tmp = talloc_realloc(NULL, program->filter, struct sock_filter, length + nb_statements); + if (tmp == NULL) + return -ENOMEM; + program->filter = tmp; + + for (i = 0; i < nb_statements; i++, length++) + memcpy(&program->filter[length], &statements[i], sizeof(struct sock_filter)); + + return 0; +} + +/** + * Append to @program->filter the statements required to notify PRoot + * about the given @syscall made by a tracee, with the given @flag. + * This function returns -errno if an error occurred, otherwise 0. + */ +static int add_trace_syscall(struct sock_fprog *program, word_t syscall, int flag) +{ + int status; + + /* Sanity check. */ + if (syscall > UINT32_MAX) + return -ERANGE; + + #define LENGTH_TRACE_SYSCALL 2 + struct sock_filter statements[LENGTH_TRACE_SYSCALL] = { + /* Compare the accumulator with the expected syscall: + * skip the next statement if not equal. */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, syscall, 0, 1), + + /* Notify the tracer. */ + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRACE + flag) + }; + + DEBUG_FILTER("FILTER: trace if syscall == %ld\n", syscall); + + status = add_statements(program, LENGTH_TRACE_SYSCALL, statements); + if (status < 0) + return status; + + return 0; +} + +/** + * Append to @program->filter the statements that allow anything (if + * unfiltered). Note that @nb_traced_syscalls is used to make a + * sanity check. This function returns -errno if an error occurred, + * otherwise 0. + */ +static int end_arch_section(struct sock_fprog *program, size_t nb_traced_syscalls) +{ + int status; + + #define LENGTH_END_SECTION 1 + struct sock_filter statements[LENGTH_END_SECTION] = { + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) + }; + + DEBUG_FILTER("FILTER: allow\n"); + + status = add_statements(program, LENGTH_END_SECTION, statements); + if (status < 0) + return status; + + /* Sanity check, see start_arch_section(). */ + if ( talloc_array_length(program->filter) - program->len + != LENGTH_END_SECTION + nb_traced_syscalls * LENGTH_TRACE_SYSCALL) + return -ERANGE; + + return 0; +} + +/** + * Append to @program->filter the statements that check the current + * @architecture. Note that @nb_traced_syscalls is used to make a + * sanity check. This function returns -errno if an error occurred, + * otherwise 0. + */ +static int start_arch_section(struct sock_fprog *program, uint32_t arch, size_t nb_traced_syscalls) +{ + const size_t arch_offset = offsetof(struct seccomp_data, arch); + const size_t syscall_offset = offsetof(struct seccomp_data, nr); + const size_t section_length = LENGTH_END_SECTION + + nb_traced_syscalls * LENGTH_TRACE_SYSCALL; + int status; + + /* Sanity checks. */ + if ( arch_offset > UINT32_MAX + || syscall_offset > UINT32_MAX + || section_length > UINT32_MAX - 1) + return -ERANGE; + + #define LENGTH_START_SECTION 4 + struct sock_filter statements[LENGTH_START_SECTION] = { + /* Load the current architecture into the + * accumulator. */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_offset), + + /* Compare the accumulator with the expected + * architecture: skip the following statement if + * equal. */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 1, 0), + + /* This is not the expected architecture, so jump + * unconditionally to the end of this section. */ + BPF_STMT(BPF_JMP + BPF_JA + BPF_K, section_length + 1), + + /* This is the expected architecture, so load the + * current syscall into the accumulator. */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_offset) + }; + + DEBUG_FILTER("FILTER: if arch == %ld, up to %zdth statement\n", + arch, nb_traced_syscalls); + + status = add_statements(program, LENGTH_START_SECTION, statements); + if (status < 0) + return status; + + /* See the sanity check in end_arch_section(). */ + program->len = talloc_array_length(program->filter); + + return 0; +} + +/** + * Append to @program->filter the statements that forbid anything (if + * unfiltered) and update @program->len. This function returns -errno + * if an error occurred, otherwise 0. + */ +static int finalize_program_filter(struct sock_fprog *program) +{ + int status; + + #define LENGTH_FINALIZE 1 + struct sock_filter statements[LENGTH_FINALIZE] = { + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL) + }; + + DEBUG_FILTER("FILTER: kill\n"); + + status = add_statements(program, LENGTH_FINALIZE, statements); + if (status < 0) + return status; + + program->len = talloc_array_length(program->filter); + + return 0; +} + +/** + * Free @program->filter and set @program->len to 0. + */ +static void free_program_filter(struct sock_fprog *program) +{ + TALLOC_FREE(program->filter); + program->len = 0; +} + +/** + * Convert the given @sysnums into BPF filters according to the + * following pseudo-code, then enabled them for the given @tracee and + * all of its future children: + * + * for each handled architectures + * for each filtered syscall + * trace + * allow + * kill + * + * This function returns -errno if an error occurred, otherwise 0. + */ +static int set_seccomp_filters(const FilteredSysnum *sysnums) +{ + SeccompArch seccomp_archs[] = SECCOMP_ARCHS; + size_t nb_archs = sizeof(seccomp_archs) / sizeof(SeccompArch); + + struct sock_fprog program = { .len = 0, .filter = NULL }; + size_t nb_traced_syscalls; + size_t i, j, k; + int status; + + status = new_program_filter(&program); + if (status < 0) + goto end; + + /* For each handled architectures */ + for (i = 0; i < nb_archs; i++) { + word_t syscall; + + nb_traced_syscalls = 0; + + /* Pre-compute the number of traced syscalls for this architecture. */ + for (j = 0; j < seccomp_archs[i].nb_abis; j++) { + for (k = 0; sysnums[k].value != PR_void; k++) { + syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value); + if (syscall != SYSCALL_AVOIDER) + nb_traced_syscalls++; + } + } + + /* Filter: if handled architecture */ + status = start_arch_section(&program, seccomp_archs[i].value, nb_traced_syscalls); + if (status < 0) + goto end; + + for (j = 0; j < seccomp_archs[i].nb_abis; j++) { + for (k = 0; sysnums[k].value != PR_void; k++) { + /* Get the architecture specific syscall number. */ + syscall = detranslate_sysnum(seccomp_archs[i].abis[j], sysnums[k].value); + if (syscall == SYSCALL_AVOIDER) + continue; + + /* Filter: trace if handled syscall */ + status = add_trace_syscall(&program, syscall, sysnums[k].flags); + if (status < 0) + goto end; + } + } + + /* Filter: allow untraced syscalls for this architecture */ + status = end_arch_section(&program, nb_traced_syscalls); + if (status < 0) + goto end; + } + + status = finalize_program_filter(&program); + if (status < 0) + goto end; + + status = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + if (status < 0) + goto end; + + /* To output this BPF program for debug purpose: + * + * write(2, program.filter, program.len * sizeof(struct sock_filter)); + */ + + status = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &program); + if (status < 0) + goto end; + + status = 0; +end: + free_program_filter(&program); + return status; +} + +/* List of sysnums handled by PRoot. */ +static FilteredSysnum proot_sysnums[] = { + { PR_accept, FILTER_SYSEXIT }, + { PR_accept4, FILTER_SYSEXIT }, + { PR_access, 0 }, + { PR_bind, 0 }, + { PR_brk, FILTER_SYSEXIT }, + { PR_chdir, FILTER_SYSEXIT }, + { PR_chmod, 0 }, + { PR_chown, 0 }, + { PR_chown32, 0 }, + { PR_connect, 0 }, + { PR_execve, FILTER_SYSEXIT }, + { PR_faccessat, 0 }, + { PR_faccessat2, 0 }, + { PR_fchdir, FILTER_SYSEXIT }, + { PR_fchmodat, 0 }, + { PR_fchownat, 0 }, + { PR_fstatat64, 0 }, + { PR_futimesat, 0 }, + { PR_getcwd, FILTER_SYSEXIT }, + { PR_getpeername, FILTER_SYSEXIT }, + { PR_getsockname, FILTER_SYSEXIT }, + { PR_getxattr, 0 }, + { PR_inotify_add_watch, 0 }, + { PR_lchown, 0 }, + { PR_lchown32, 0 }, + { PR_lgetxattr, 0 }, + { PR_link, 0 }, + { PR_linkat, 0 }, + { PR_listxattr, 0 }, + { PR_llistxattr, 0 }, + { PR_lremovexattr, 0 }, + { PR_lsetxattr, 0 }, + { PR_lstat, 0 }, + { PR_lstat64, 0 }, + { PR_mkdir, 0 }, + { PR_mkdirat, 0 }, + { PR_mknod, 0 }, + { PR_mknodat, 0 }, + { PR_name_to_handle_at, 0 }, + { PR_newfstatat, 0 }, + { PR_open, 0 }, + { PR_openat, 0 }, + { PR_prctl, 0 }, + { PR_prlimit64, FILTER_SYSEXIT }, + { PR_ptrace, FILTER_SYSEXIT }, + { PR_readlink, FILTER_SYSEXIT }, + { PR_readlinkat, FILTER_SYSEXIT }, + { PR_removexattr, 0 }, + { PR_rename, FILTER_SYSEXIT }, + { PR_renameat, FILTER_SYSEXIT }, + { PR_renameat2, FILTER_SYSEXIT }, + { PR_rmdir, 0 }, + { PR_setrlimit, FILTER_SYSEXIT }, + { PR_setxattr, 0 }, + { PR_stat, 0 }, + { PR_stat64, 0 }, + { PR_statfs, FILTER_SYSEXIT }, + { PR_statfs64, FILTER_SYSEXIT }, + { PR_symlink, 0 }, + { PR_symlinkat, 0 }, + { PR_truncate, 0 }, + { PR_truncate64, 0 }, + { PR_uname, FILTER_SYSEXIT }, + { PR_unlink, 0 }, + { PR_unlinkat, 0 }, + { PR_utime, FILTER_SYSEXIT }, + { PR_utimensat, 0 }, + { PR_utimes, 0 }, + { PR_wait4, FILTER_SYSEXIT }, + { PR_waitpid, FILTER_SYSEXIT }, + FILTERED_SYSNUM_END, +}; + +/** + * Add the @new_sysnums to the list of filtered @sysnums, using the + * given Talloc @context. This function returns -errno if an error + * occurred, otherwise 0. + */ +static int merge_filtered_sysnums(TALLOC_CTX *context, FilteredSysnum **sysnums, + const FilteredSysnum *new_sysnums) +{ + size_t i, j; + + assert(sysnums != NULL); + + if (*sysnums == NULL) { + /* Start with no sysnums but the terminator. */ + *sysnums = talloc_array(context, FilteredSysnum, 1); + if (*sysnums == NULL) + return -ENOMEM; + + (*sysnums)[0].value = PR_void; + } + + for (i = 0; new_sysnums[i].value != PR_void; i++) { + /* Search for the given sysnum. */ + for (j = 0; (*sysnums)[j].value != PR_void + && (*sysnums)[j].value != new_sysnums[i].value; j++) + ; + + if ((*sysnums)[j].value == PR_void) { + /* No such sysnum, allocate a new entry. */ + (*sysnums) = talloc_realloc(context, (*sysnums), FilteredSysnum, j + 2); + if ((*sysnums) == NULL) + return -ENOMEM; + + (*sysnums)[j] = new_sysnums[i]; + + /* The last item is the terminator. */ + (*sysnums)[j + 1].value = PR_void; + } + else + /* The sysnum is already filtered, merge the + * flags. */ + (*sysnums)[j].flags |= new_sysnums[i].flags; + } + + return 0; +} + +/** + * Tell the kernel to trace only syscalls handled by PRoot. + * This filter will be enabled for the given @tracee and + * all of its future children. This function returns -errno if an + * error occurred, otherwise 0. + */ +int enable_syscall_filtering(const Tracee *tracee) +{ + FilteredSysnum *filtered_sysnums = NULL; + int status; + + assert(tracee != NULL && tracee->ctx != NULL); + + /* Add the sysnums required by PRoot to the list of filtered sysnums. */ + status = merge_filtered_sysnums(tracee->ctx, &filtered_sysnums, proot_sysnums); + if (status < 0) + return status; + + status = set_seccomp_filters(filtered_sysnums); + if (status < 0) + return status; + + return 0; +} diff --git a/app/src/main/cpp/proot/src/syscall/seccomp.h b/app/src/main/cpp/proot/src/syscall/seccomp.h new file mode 100644 index 0000000..3d5ba40 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/seccomp.h @@ -0,0 +1,48 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef SECCOMP_H +#define SECCOMP_H + +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "attribute.h" +#include "arch.h" + +typedef struct { + Sysnum value; + word_t flags; +} FilteredSysnum; + +typedef struct { + unsigned int value; + size_t nb_abis; + Abi abis[NB_MAX_ABIS]; +} SeccompArch; + +#define FILTERED_SYSNUM_END { PR_void, 0 } + +#define FILTER_SYSEXIT 0x1 + +extern int enable_syscall_filtering(const Tracee *tracee); + +#endif /* SECCOMP_H */ diff --git a/app/src/main/cpp/proot/src/syscall/socket.c b/app/src/main/cpp/proot/src/syscall/socket.c new file mode 100644 index 0000000..6aa291a --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/socket.c @@ -0,0 +1,216 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* offsetof(3), */ +#include /* bzero(3), */ +#include /* strncpy(3), strlen(3), */ +#include /* assert(3), */ +#include /* E*, */ +#include /* struct sockaddr_un, AF_UNIX, */ +#include /* struct sockaddr_un, */ +#include /* MIN(), MAX(), */ + +#include "syscall/socket.h" +#include "tracee/tracee.h" +#include "tracee/mem.h" +#include "path/binding.h" +#include "path/temp.h" +#include "path/path.h" +#include "arch.h" + +#include "compat.h" + +/* The sockaddr_un structure has exactly the same layout on all + * architectures. */ +static const off_t offsetof_path = offsetof(struct sockaddr_un, sun_path); +extern struct sockaddr_un sockaddr_un__; +static const size_t sizeof_path = sizeof(sockaddr_un__.sun_path); + +/** + * Copy in @sockaddr the struct sockaddr_un stored in the @tracee + * memory at the given @address. Also, its pathname is copied to the + * null-terminated @path. Only @size bytes are read from the @tracee + * memory (should be <= @max_size <= sizeof(struct sockaddr_un)). + * This function returns -errno if an error occurred, 0 if the + * structure was not found (not a sockaddr_un or @size > @max_size), + * otherwise 1. + */ +static int read_sockaddr_un(Tracee *tracee, struct sockaddr_un *sockaddr, word_t max_size, + char path[PATH_MAX], word_t address, int size) +{ + int status; + + assert(max_size <= sizeof(struct sockaddr_un)); + + /* Nothing to do if the sockaddr has an unexpected size. */ + if (size <= offsetof_path || (word_t) size > max_size) + return 0; + + bzero(sockaddr, sizeof(struct sockaddr_un)); + status = read_data(tracee, sockaddr, address, size); + if (status < 0) + return status; + + /* Nothing to do if it's not a named Unix domain socket. */ + if ((sockaddr->sun_family != AF_UNIX) + || sockaddr->sun_path[0] == '\0') + return 0; + + /* Be careful: sun_path doesn't have to be null-terminated. */ + assert(sizeof_path < PATH_MAX - 1); + strncpy(path, sockaddr->sun_path, sizeof_path); + path[sizeof_path] = '\0'; + + return 1; +} + +/** + * Translate the pathname of the struct sockaddr_un currently stored + * in the @tracee memory at the given @address. See the documentation + * of read_sockaddr_un() for the meaning of the @size parameter. + * Also, the new address of the translated sockaddr_un is put in the + * @address parameter. This function returns -errno if an error + * occurred, otherwise 0. + */ +int translate_socketcall_enter(Tracee *tracee, word_t *address, int size) +{ + struct sockaddr_un sockaddr; + char user_path[PATH_MAX]; + char host_path[PATH_MAX]; + int status; + + if (*address == 0) + return 0; + + status = read_sockaddr_un(tracee, &sockaddr, sizeof(sockaddr), user_path, *address, size); + if (status <= 0) + return status; + + status = translate_path(tracee, host_path, AT_FDCWD, user_path, true); + if (status < 0) + return status; + + /* Be careful: sun_path doesn't have to be null-terminated. */ + if (strlen(host_path) > sizeof_path) { + char *shorter_host_path; + Binding *binding; + + /* The translated path is too long to fit the sun_path + * array, so let's bind it to a shorter path. */ + shorter_host_path = create_temp_name(tracee->ctx, "proot"); + if (shorter_host_path == NULL || strlen(shorter_host_path) > sizeof_path) + return -EINVAL; + + (void) mkstemp(shorter_host_path); + + if (strlen(shorter_host_path) > sizeof_path) + return -EINVAL; + + /* Ensure the guest path of this new binding is + * canonicalized, as it is always assumed. */ + strcpy(user_path, host_path); + status = detranslate_path(tracee, user_path, NULL); + if (status < 0) + return -EINVAL; + + /* Bing the guest path to a shorter host path. */ + binding = insort_binding3(tracee, tracee->ctx, shorter_host_path, user_path); + if (binding == NULL) + return -EINVAL; + + /* This temporary file (shorter_host_path) will be removed once the + * binding is destroyed. */ + talloc_reparent(tracee->ctx, binding, shorter_host_path); + + /* Let's use this shorter path now. */ + strcpy(host_path, shorter_host_path); + } + strncpy(sockaddr.sun_path, host_path, sizeof_path); + + /* Push the updated sockaddr to a newly allocated space. */ + *address = alloc_mem(tracee, sizeof(sockaddr)); + if (*address == 0) + return -EFAULT; + + status = write_data(tracee, *address, &sockaddr, sizeof(sockaddr)); + if (status < 0) + return status; + + return 1; +} + +/** + * Detranslate the pathname of the struct sockaddr_un currently stored + * in the @tracee memory at the given @sock_addr. See the + * documentation of read_sockaddr_un() for the meaning of the + * @size_addr and @max_size parameters. This function returns -errno + * if an error occurred, otherwise 0. + */ +int translate_socketcall_exit(Tracee *tracee, word_t sock_addr, word_t size_addr, word_t max_size) +{ + struct sockaddr_un sockaddr; + bool is_truncated = false; + char path[PATH_MAX]; + int status; + int size; + + if (sock_addr == 0) + return 0; + + size = peek_int32(tracee, size_addr); + if (errno != 0) + return -errno; + + max_size = MIN(max_size, sizeof(sockaddr)); + status = read_sockaddr_un(tracee, &sockaddr, max_size, path, sock_addr, size); + if (status <= 0) + return status; + + status = detranslate_path(tracee, path, NULL); + if (status < 0) + return status; + + /* Be careful: sun_path doesn't have to be null-terminated. */ + size = offsetof_path + strlen(path) + 1; + if (size < 0 || (word_t) size > max_size) { + size = max_size; + is_truncated = true; + } + strncpy(sockaddr.sun_path, path, sizeof_path); + + /* Overwrite the sockaddr and socklen parameters. */ + status = write_data(tracee, sock_addr, &sockaddr, size); + if (status < 0) + return status; + + /* If sockaddr is truncated (because the buffer provided is + * too small), addrlen will return a value greater than was + * supplied to the call. See man 2 accept. */ + if (is_truncated) + size = max_size + 1; + + poke_int32(tracee, size_addr, size); + if (errno != 0) + return -errno; + + return 0; +} diff --git a/app/src/main/cpp/proot/src/syscall/socket.h b/app/src/main/cpp/proot/src/syscall/socket.h new file mode 100644 index 0000000..8c16cb1 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/socket.h @@ -0,0 +1,32 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef SOCKET_H +#define SOCKET_H + +#include "arch.h" /* word_t */ +#include "tracee/tracee.h" + +int translate_socketcall_enter(Tracee *tracee, word_t *sock_addr, int size); +int translate_socketcall_exit(Tracee *tracee, word_t sock_addr, word_t size_addr, word_t max_size); + +#endif /* SOCKET_H */ diff --git a/app/src/main/cpp/proot/src/syscall/syscall.c b/app/src/main/cpp/proot/src/syscall/syscall.c new file mode 100644 index 0000000..722b1ca --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/syscall.c @@ -0,0 +1,233 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* assert(3), */ +#include /* PATH_MAX, */ +#include /* strlen(3), */ +#include /* errno(3), E* */ + +#include "syscall/syscall.h" +#include "syscall/chain.h" +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "tracee/mem.h" +#include "cli/note.h" + +/** + * Copy in @path a C string (PATH_MAX bytes max.) from the @tracee's + * memory address space pointed to by the @reg argument of the + * current syscall. This function returns -errno if an error occured, + * otherwise it returns the size in bytes put into the @path. + */ +int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg) +{ + int size; + word_t src; + + src = peek_reg(tracee, CURRENT, reg); + + /* Check if the parameter is not NULL. Technically we should + * not return an -EFAULT for this special value since it is + * allowed for some syscall, utimensat(2) for instance. */ + if (src == 0) { + path[0] = '\0'; + return 0; + } + + /* Get the path from the tracee's memory space. */ + size = read_path(tracee, path, src); + if (size < 0) + return size; + + path[size] = '\0'; + return size; +} + +/** + * Copy @size bytes of the data pointed to by @tracer_ptr into a + * @tracee's memory block and make the @reg argument of the current + * syscall points to this new block. This function returns -errno if + * an error occured, otherwise 0. + */ +int set_sysarg_data(Tracee *tracee, const void *tracer_ptr, word_t size, Reg reg) +{ + word_t tracee_ptr; + int status; + + /* Allocate space into the tracee's memory to host the new data. */ + tracee_ptr = alloc_mem(tracee, size); + if (tracee_ptr == 0) + return -EFAULT; + + /* Copy the new data into the previously allocated space. */ + status = write_data(tracee, tracee_ptr, tracer_ptr, size); + if (status < 0) + return status; + + /* Make this argument point to the new data. */ + poke_reg(tracee, reg, tracee_ptr); + + return 0; +} + +/** + * Copy @path to a @tracee's memory block and make the @reg argument + * of the current syscall points to this new block. This function + * returns -errno if an error occured, otherwise 0. + */ +int set_sysarg_path(Tracee *tracee, const char path[PATH_MAX], Reg reg) +{ + return set_sysarg_data(tracee, path, strlen(path) + 1, reg); +} + +void translate_syscall(Tracee *tracee) +{ + const bool is_enter_stage = IS_IN_SYSENTER(tracee); + int status; + + assert(tracee->exe != NULL); + + status = fetch_regs(tracee); + if (status < 0) + return; + + int suppressed_syscall_status = 0; + + if (is_enter_stage) { + /* Never restore original register values at the end + * of this stage. */ + tracee->restore_original_regs = false; + + print_current_regs(tracee, 3, "sysenter start"); + + /* Translate the syscall only if it was actually + * requested by the tracee, it is not a syscall + * chained by PRoot. */ + if (tracee->chain.syscalls == NULL) { + save_current_regs(tracee, ORIGINAL); + status = translate_syscall_enter(tracee); + save_current_regs(tracee, MODIFIED); + } + else { + if (tracee->chain.sysnum_workaround_state != SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL) + status = 0; + tracee->restart_how = PTRACE_SYSCALL; + } + + /* Remember the tracee status for the "exit" stage and + * avoid the actual syscall if an error was reported + * by the translation. */ + if (status < 0) { + set_sysnum(tracee, PR_void); + poke_reg(tracee, SYSARG_RESULT, (word_t) status); + tracee->status = status; +#if defined(ARCH_ARM_EABI) + tracee->restart_how = PTRACE_SYSCALL; +#endif + } + else + tracee->status = 1; + + /* Restore tracee's stack pointer now if it won't hit + * the sysexit stage (i.e. when seccomp is enabled and + * there's nothing else to do). */ + if (tracee->restart_how == PTRACE_CONT) { + suppressed_syscall_status = tracee->status; + tracee->status = 0; + poke_reg(tracee, STACK_POINTER, peek_reg(tracee, ORIGINAL, STACK_POINTER)); + } + } + else { + /* By default, restore original register values at the + * end of this stage. */ + tracee->restore_original_regs = true; + + print_current_regs(tracee, 5, "sysexit start"); + + /* Translate the syscall only if it was actually + * requested by the tracee, it is not a syscall + * chained by PRoot. */ + if (tracee->chain.syscalls == NULL || tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL) { + tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_INACTIVE; + translate_syscall_exit(tracee); + } + else if (tracee->chain.sysnum_workaround_state == SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL) { + tracee->chain.sysnum_workaround_state = SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL; + } + + /* Reset the tracee's status. */ + tracee->status = 0; + + /* Insert the next chained syscall, if any. */ + if (tracee->chain.syscalls != NULL) + chain_next_syscall(tracee); + } + + bool override_sysnum = is_enter_stage && tracee->chain.syscalls == NULL; + int push_regs_status = push_specific_regs(tracee, override_sysnum); + + /* Handle inability to change syscall number */ + if (push_regs_status < 0 && override_sysnum) { + word_t orig_sysnum = peek_reg(tracee, ORIGINAL, SYSARG_NUM); + word_t current_sysnum = peek_reg(tracee, CURRENT, SYSARG_NUM); + print_current_regs(tracee, 4, "pre_push"); + if (orig_sysnum != current_sysnum) { + /* Restart current syscall as chained */ + if (current_sysnum != SYSCALL_AVOIDER) { + restart_current_syscall_as_chained(tracee); + } else if (suppressed_syscall_status) { + /* If we've decided to fail this syscall + * by setting it to no-op and continuing, but turns out + * that we can't just make syscall nop, restore tracee->status + * and intercept syscall exit */ + tracee->status = suppressed_syscall_status; + tracee->restart_how = PTRACE_SYSCALL; + } + + /* Set syscall arguments to make it fail + * For most syscalls we set all args to -1 + * Hoping there is among them invalid request/address/fd/value that will make syscall fail */ + poke_reg(tracee, SYSARG_1, -1); + poke_reg(tracee, SYSARG_2, -1); + poke_reg(tracee, SYSARG_3, -1); + poke_reg(tracee, SYSARG_4, -1); + poke_reg(tracee, SYSARG_5, -1); + poke_reg(tracee, SYSARG_6, -1); + + if (get_sysnum(tracee, ORIGINAL) == PR_brk) { + /* For brk() we pass 0 as first arg; this is used to query value without changing it */ + poke_reg(tracee, SYSARG_1, 0); + } + + /* Push regs again without changing syscall */ + push_regs_status = push_specific_regs(tracee, false); + if (push_regs_status != 0) { + note(tracee, WARNING, SYSTEM, "can't set tracee registers in workaround"); + } + } + } + + if (is_enter_stage) + print_current_regs(tracee, 5, "sysenter end" ); + else + print_current_regs(tracee, 4, "sysexit end"); +} diff --git a/app/src/main/cpp/proot/src/syscall/syscall.h b/app/src/main/cpp/proot/src/syscall/syscall.h new file mode 100644 index 0000000..e255cc8 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/syscall.h @@ -0,0 +1,39 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef SYSCALL_H +#define SYSCALL_H + +#include /* PATH_MAX, */ + +#include "tracee/tracee.h" +#include "tracee/reg.h" + +extern int get_sysarg_path(const Tracee *tracee, char path[PATH_MAX], Reg reg); +extern int set_sysarg_path(Tracee *tracee, const char path[PATH_MAX], Reg reg); +extern int set_sysarg_data(Tracee *tracee, const void *tracer_ptr, word_t size, Reg reg); + +extern void translate_syscall(Tracee *tracee); +extern int translate_syscall_enter(Tracee *tracee); +extern void translate_syscall_exit(Tracee *tracee); + +#endif /* SYSCALL_H */ diff --git a/app/src/main/cpp/proot/src/syscall/sysnum.c b/app/src/main/cpp/proot/src/syscall/sysnum.c new file mode 100644 index 0000000..5014aa8 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/sysnum.c @@ -0,0 +1,150 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include + +#include "syscall/sysnum.h" +#include "tracee/tracee.h" +#include "tracee/abi.h" +#include "tracee/reg.h" +#include "arch.h" +#include "cli/note.h" + +#include SYSNUMS_HEADER1 + +#ifdef SYSNUMS_HEADER2 +#include SYSNUMS_HEADER2 +#endif + +typedef struct { + const Sysnum *table; + word_t offset; + word_t length; +} Sysnums; + +/** + * Update @sysnums' fields with the sysnum table for the given @abi. + */ +static void get_sysnums(Abi abi, Sysnums *sysnums) +{ + switch (abi) { + case ABI_DEFAULT: + sysnums->table = SYSNUMS_ABI1; + sysnums->length = sizeof(SYSNUMS_ABI1) / sizeof(Sysnum); + sysnums->offset = 0; + return; +#ifdef SYSNUMS_ABI2 + case ABI_2: + sysnums->table = SYSNUMS_ABI2; + sysnums->length = sizeof(SYSNUMS_ABI2) / sizeof(Sysnum); + sysnums->offset = 0; + return; +#endif + default: + assert(0); + } +} + +/** + * Return the neutral value of @sysnum from the given @abi. + */ +static Sysnum translate_sysnum(Abi abi, word_t sysnum) +{ + Sysnums sysnums; + word_t index; + + get_sysnums(abi, &sysnums); + + /* Sanity checks. */ + if (sysnum < sysnums.offset) + return PR_void; + + index = sysnum - sysnums.offset; + + /* Sanity checks. */ + if (index > sysnums.length) + return PR_void; + + return sysnums.table[index]; +} + +/** + * Return the architecture value of @sysnum for the given @abi. + */ +word_t detranslate_sysnum(Abi abi, Sysnum sysnum) +{ + Sysnums sysnums; + size_t i; + + /* Very special case. */ + if (sysnum == PR_void) + return SYSCALL_AVOIDER; + + get_sysnums(abi, &sysnums); + + for (i = 0; i < sysnums.length; i++) { + if (sysnums.table[i] != sysnum) + continue; + + return i + sysnums.offset; + } + + return SYSCALL_AVOIDER; +} + +/** + * Return the neutral value of the @tracee's current syscall number. + */ +Sysnum get_sysnum(const Tracee *tracee, RegVersion version) +{ + return translate_sysnum(get_abi(tracee), peek_reg(tracee, version, SYSARG_NUM)); +} + +/** + * Overwrite the @tracee's current syscall number with @sysnum. Note: + * this neutral value is automatically converted into the architecture + * value. + */ +void set_sysnum(Tracee *tracee, Sysnum sysnum) +{ + poke_reg(tracee, SYSARG_NUM, detranslate_sysnum(get_abi(tracee), sysnum)); +} + +/** + * Return the human readable name of @sysnum. + */ +const char *stringify_sysnum(Sysnum sysnum) +{ + #define SYSNUM(item) [ PR_ ## item ] = #item, + static const char *names[] = { + #include "syscall/sysnums.list" + }; + #undef SYSNUM + + if (sysnum == 0) + return "void"; + + if (sysnum >= PR_NB_SYSNUM) + return ""; + + return names[sysnum]; +} diff --git a/app/src/main/cpp/proot/src/syscall/sysnum.h b/app/src/main/cpp/proot/src/syscall/sysnum.h new file mode 100644 index 0000000..8d56ab3 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/sysnum.h @@ -0,0 +1,45 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef SYSNUM_H +#define SYSNUM_H + +#include + +#include "tracee/tracee.h" +#include "tracee/abi.h" +#include "tracee/reg.h" + +#define SYSNUM(item) PR_ ## item, +typedef enum { + PR_void = 0, + #include "syscall/sysnums.list" + PR_NB_SYSNUM +} Sysnum; +#undef SYSNUM + +extern Sysnum get_sysnum(const Tracee *tracee, RegVersion version); +extern void set_sysnum(Tracee *tracee, Sysnum sysnum); +extern word_t detranslate_sysnum(Abi abi, Sysnum sysnum); +extern const char *stringify_sysnum(Sysnum sysnum); + +#endif /* SYSNUM_H */ diff --git a/app/src/main/cpp/proot/src/syscall/sysnums-arm.h b/app/src/main/cpp/proot/src/syscall/sysnums-arm.h new file mode 100644 index 0000000..30d0d75 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/sysnums-arm.h @@ -0,0 +1,360 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_arm[] = { + [ 0 ] = PR_restart_syscall, + [ 1 ] = PR_exit, + [ 2 ] = PR_fork, + [ 3 ] = PR_read, + [ 4 ] = PR_write, + [ 5 ] = PR_open, + [ 6 ] = PR_close, + [ 8 ] = PR_creat, + [ 9 ] = PR_link, + [ 10 ] = PR_unlink, + [ 11 ] = PR_execve, + [ 12 ] = PR_chdir, + [ 14 ] = PR_mknod, + [ 15 ] = PR_chmod, + [ 16 ] = PR_lchown, + [ 19 ] = PR_lseek, + [ 20 ] = PR_getpid, + [ 21 ] = PR_mount, + [ 23 ] = PR_setuid, + [ 24 ] = PR_getuid, + [ 26 ] = PR_ptrace, + [ 29 ] = PR_pause, + [ 30 ] = PR_utime, + [ 33 ] = PR_access, + [ 34 ] = PR_nice, + [ 36 ] = PR_sync, + [ 37 ] = PR_kill, + [ 38 ] = PR_rename, + [ 39 ] = PR_mkdir, + [ 40 ] = PR_rmdir, + [ 41 ] = PR_dup, + [ 42 ] = PR_pipe, + [ 43 ] = PR_times, + [ 45 ] = PR_brk, + [ 46 ] = PR_setgid, + [ 47 ] = PR_getgid, + [ 49 ] = PR_geteuid, + [ 50 ] = PR_getegid, + [ 51 ] = PR_acct, + [ 52 ] = PR_umount2, + [ 54 ] = PR_ioctl, + [ 55 ] = PR_fcntl, + [ 57 ] = PR_setpgid, + [ 60 ] = PR_umask, + [ 61 ] = PR_chroot, + [ 62 ] = PR_ustat, + [ 63 ] = PR_dup2, + [ 64 ] = PR_getppid, + [ 65 ] = PR_getpgrp, + [ 66 ] = PR_setsid, + [ 67 ] = PR_sigaction, + [ 70 ] = PR_setreuid, + [ 71 ] = PR_setregid, + [ 72 ] = PR_sigsuspend, + [ 73 ] = PR_sigpending, + [ 74 ] = PR_sethostname, + [ 75 ] = PR_setrlimit, + [ 77 ] = PR_getrusage, + [ 78 ] = PR_gettimeofday, + [ 79 ] = PR_settimeofday, + [ 80 ] = PR_getgroups, + [ 81 ] = PR_setgroups, + [ 83 ] = PR_symlink, + [ 85 ] = PR_readlink, + [ 86 ] = PR_uselib, + [ 87 ] = PR_swapon, + [ 88 ] = PR_reboot, + [ 91 ] = PR_munmap, + [ 92 ] = PR_truncate, + [ 93 ] = PR_ftruncate, + [ 94 ] = PR_fchmod, + [ 95 ] = PR_fchown, + [ 96 ] = PR_getpriority, + [ 97 ] = PR_setpriority, + [ 99 ] = PR_statfs, + [ 100 ] = PR_fstatfs, + [ 103 ] = PR_syslog, + [ 104 ] = PR_setitimer, + [ 105 ] = PR_getitimer, + [ 106 ] = PR_stat, + [ 107 ] = PR_lstat, + [ 108 ] = PR_fstat, + [ 111 ] = PR_vhangup, + [ 114 ] = PR_wait4, + [ 115 ] = PR_swapoff, + [ 116 ] = PR_sysinfo, + [ 118 ] = PR_fsync, + [ 119 ] = PR_sigreturn, + [ 120 ] = PR_clone, + [ 121 ] = PR_setdomainname, + [ 122 ] = PR_uname, + [ 124 ] = PR_adjtimex, + [ 125 ] = PR_mprotect, + [ 126 ] = PR_sigprocmask, + [ 128 ] = PR_init_module, + [ 129 ] = PR_delete_module, + [ 131 ] = PR_quotactl, + [ 132 ] = PR_getpgid, + [ 133 ] = PR_fchdir, + [ 134 ] = PR_bdflush, + [ 135 ] = PR_sysfs, + [ 136 ] = PR_personality, + [ 138 ] = PR_setfsuid, + [ 139 ] = PR_setfsgid, + [ 140 ] = PR__llseek, + [ 141 ] = PR_getdents, + [ 142 ] = PR__newselect, + [ 143 ] = PR_flock, + [ 144 ] = PR_msync, + [ 145 ] = PR_readv, + [ 146 ] = PR_writev, + [ 147 ] = PR_getsid, + [ 148 ] = PR_fdatasync, + [ 149 ] = PR__sysctl, + [ 150 ] = PR_mlock, + [ 151 ] = PR_munlock, + [ 152 ] = PR_mlockall, + [ 153 ] = PR_munlockall, + [ 154 ] = PR_sched_setparam, + [ 155 ] = PR_sched_getparam, + [ 156 ] = PR_sched_setscheduler, + [ 157 ] = PR_sched_getscheduler, + [ 158 ] = PR_sched_yield, + [ 159 ] = PR_sched_get_priority_max, + [ 160 ] = PR_sched_get_priority_min, + [ 161 ] = PR_sched_rr_get_interval, + [ 162 ] = PR_nanosleep, + [ 163 ] = PR_mremap, + [ 164 ] = PR_setresuid, + [ 165 ] = PR_getresuid, + [ 168 ] = PR_poll, + [ 169 ] = PR_nfsservctl, + [ 170 ] = PR_setresgid, + [ 171 ] = PR_getresgid, + [ 172 ] = PR_prctl, + [ 173 ] = PR_rt_sigreturn, + [ 174 ] = PR_rt_sigaction, + [ 175 ] = PR_rt_sigprocmask, + [ 176 ] = PR_rt_sigpending, + [ 177 ] = PR_rt_sigtimedwait, + [ 178 ] = PR_rt_sigqueueinfo, + [ 179 ] = PR_rt_sigsuspend, + [ 180 ] = PR_pread64, + [ 181 ] = PR_pwrite64, + [ 182 ] = PR_chown, + [ 183 ] = PR_getcwd, + [ 184 ] = PR_capget, + [ 185 ] = PR_capset, + [ 186 ] = PR_sigaltstack, + [ 187 ] = PR_sendfile, + [ 190 ] = PR_vfork, + [ 191 ] = PR_ugetrlimit, + [ 192 ] = PR_mmap2, + [ 193 ] = PR_truncate64, + [ 194 ] = PR_ftruncate64, + [ 195 ] = PR_stat64, + [ 196 ] = PR_lstat64, + [ 197 ] = PR_fstat64, + [ 198 ] = PR_lchown32, + [ 199 ] = PR_getuid32, + [ 200 ] = PR_getgid32, + [ 201 ] = PR_geteuid32, + [ 202 ] = PR_getegid32, + [ 203 ] = PR_setreuid32, + [ 204 ] = PR_setregid32, + [ 205 ] = PR_getgroups32, + [ 206 ] = PR_setgroups32, + [ 207 ] = PR_fchown32, + [ 208 ] = PR_setresuid32, + [ 209 ] = PR_getresuid32, + [ 210 ] = PR_setresgid32, + [ 211 ] = PR_getresgid32, + [ 212 ] = PR_chown32, + [ 213 ] = PR_setuid32, + [ 214 ] = PR_setgid32, + [ 215 ] = PR_setfsuid32, + [ 216 ] = PR_setfsgid32, + [ 217 ] = PR_getdents64, + [ 218 ] = PR_pivot_root, + [ 219 ] = PR_mincore, + [ 220 ] = PR_madvise, + [ 221 ] = PR_fcntl64, + [ 222 ] = PR_void, + [ 224 ] = PR_gettid, + [ 225 ] = PR_readahead, + [ 226 ] = PR_setxattr, + [ 227 ] = PR_lsetxattr, + [ 228 ] = PR_fsetxattr, + [ 229 ] = PR_getxattr, + [ 230 ] = PR_lgetxattr, + [ 231 ] = PR_fgetxattr, + [ 232 ] = PR_listxattr, + [ 233 ] = PR_llistxattr, + [ 234 ] = PR_flistxattr, + [ 235 ] = PR_removexattr, + [ 236 ] = PR_lremovexattr, + [ 237 ] = PR_fremovexattr, + [ 238 ] = PR_tkill, + [ 239 ] = PR_sendfile64, + [ 240 ] = PR_futex, + [ 241 ] = PR_sched_setaffinity, + [ 242 ] = PR_sched_getaffinity, + [ 243 ] = PR_io_setup, + [ 244 ] = PR_io_destroy, + [ 245 ] = PR_io_getevents, + [ 246 ] = PR_io_submit, + [ 247 ] = PR_io_cancel, + [ 248 ] = PR_exit_group, + [ 249 ] = PR_lookup_dcookie, + [ 250 ] = PR_epoll_create, + [ 251 ] = PR_epoll_ctl, + [ 252 ] = PR_epoll_wait, + [ 253 ] = PR_remap_file_pages, + [ 256 ] = PR_set_tid_address, + [ 257 ] = PR_timer_create, + [ 258 ] = PR_timer_settime, + [ 259 ] = PR_timer_gettime, + [ 260 ] = PR_timer_getoverrun, + [ 261 ] = PR_timer_delete, + [ 262 ] = PR_clock_settime, + [ 263 ] = PR_clock_gettime, + [ 264 ] = PR_clock_getres, + [ 265 ] = PR_clock_nanosleep, + [ 266 ] = PR_statfs64, + [ 267 ] = PR_fstatfs64, + [ 268 ] = PR_tgkill, + [ 269 ] = PR_utimes, + [ 270 ] = PR_arm_fadvise64_64, + [ 271 ] = PR_pciconfig_iobase, + [ 272 ] = PR_pciconfig_read, + [ 273 ] = PR_pciconfig_write, + [ 274 ] = PR_mq_open, + [ 275 ] = PR_mq_unlink, + [ 276 ] = PR_mq_timedsend, + [ 277 ] = PR_mq_timedreceive, + [ 278 ] = PR_mq_notify, + [ 279 ] = PR_mq_getsetattr, + [ 280 ] = PR_waitid, + [ 281 ] = PR_socket, + [ 282 ] = PR_bind, + [ 283 ] = PR_connect, + [ 284 ] = PR_listen, + [ 285 ] = PR_accept, + [ 286 ] = PR_getsockname, + [ 287 ] = PR_getpeername, + [ 288 ] = PR_socketpair, + [ 289 ] = PR_send, + [ 290 ] = PR_sendto, + [ 291 ] = PR_recv, + [ 292 ] = PR_recvfrom, + [ 293 ] = PR_shutdown, + [ 294 ] = PR_setsockopt, + [ 295 ] = PR_getsockopt, + [ 296 ] = PR_sendmsg, + [ 297 ] = PR_recvmsg, + [ 298 ] = PR_semop, + [ 299 ] = PR_semget, + [ 300 ] = PR_semctl, + [ 301 ] = PR_msgsnd, + [ 302 ] = PR_msgrcv, + [ 303 ] = PR_msgget, + [ 304 ] = PR_msgctl, + [ 305 ] = PR_shmat, + [ 306 ] = PR_shmdt, + [ 307 ] = PR_shmget, + [ 308 ] = PR_shmctl, + [ 309 ] = PR_add_key, + [ 310 ] = PR_request_key, + [ 311 ] = PR_keyctl, + [ 312 ] = PR_semtimedop, + [ 313 ] = PR_vserver, + [ 314 ] = PR_ioprio_set, + [ 315 ] = PR_ioprio_get, + [ 316 ] = PR_inotify_init, + [ 317 ] = PR_inotify_add_watch, + [ 318 ] = PR_inotify_rm_watch, + [ 319 ] = PR_mbind, + [ 320 ] = PR_get_mempolicy, + [ 321 ] = PR_set_mempolicy, + [ 322 ] = PR_openat, + [ 323 ] = PR_mkdirat, + [ 324 ] = PR_mknodat, + [ 325 ] = PR_fchownat, + [ 326 ] = PR_futimesat, + [ 327 ] = PR_fstatat64, + [ 328 ] = PR_unlinkat, + [ 329 ] = PR_renameat, + [ 330 ] = PR_linkat, + [ 331 ] = PR_symlinkat, + [ 332 ] = PR_readlinkat, + [ 333 ] = PR_fchmodat, + [ 334 ] = PR_faccessat, + [ 335 ] = PR_pselect6, + [ 336 ] = PR_ppoll, + [ 337 ] = PR_unshare, + [ 338 ] = PR_set_robust_list, + [ 339 ] = PR_get_robust_list, + [ 340 ] = PR_splice, + [ 341 ] = PR_arm_sync_file_range, + [ 342 ] = PR_tee, + [ 343 ] = PR_vmsplice, + [ 344 ] = PR_move_pages, + [ 345 ] = PR_getcpu, + [ 346 ] = PR_epoll_pwait, + [ 347 ] = PR_kexec_load, + [ 348 ] = PR_utimensat, + [ 349 ] = PR_signalfd, + [ 350 ] = PR_timerfd_create, + [ 351 ] = PR_eventfd, + [ 352 ] = PR_fallocate, + [ 353 ] = PR_timerfd_settime, + [ 354 ] = PR_timerfd_gettime, + [ 355 ] = PR_signalfd4, + [ 356 ] = PR_eventfd2, + [ 357 ] = PR_epoll_create1, + [ 358 ] = PR_dup3, + [ 359 ] = PR_pipe2, + [ 360 ] = PR_inotify_init1, + [ 361 ] = PR_preadv, + [ 362 ] = PR_pwritev, + [ 363 ] = PR_rt_tgsigqueueinfo, + [ 364 ] = PR_perf_event_open, + [ 365 ] = PR_recvmmsg, + [ 366 ] = PR_accept4, + [ 367 ] = PR_fanotify_init, + [ 368 ] = PR_fanotify_mark, + [ 369 ] = PR_prlimit64, + [ 370 ] = PR_name_to_handle_at, + [ 371 ] = PR_open_by_handle_at, + [ 372 ] = PR_clock_adjtime, + [ 373 ] = PR_syncfs, + [ 374 ] = PR_sendmmsg, + [ 375 ] = PR_setns, + [ 376 ] = PR_process_vm_readv, + [ 377 ] = PR_process_vm_writev, + [ 378 ] = PR_kcmp, + [ 379 ] = PR_finit_module, + [ 380 ] = PR_sched_setattr, + [ 381 ] = PR_sched_getattr, + [ 382 ] = PR_renameat2, + [ 383 ] = PR_seccomp, + [ 384 ] = PR_getrandom, + [ 385 ] = PR_memfd_create, + [ 386 ] = PR_bpf, + [ 387 ] = PR_execveat, + [ 388 ] = PR_userfaultfd, + [ 389 ] = PR_membarrier, + [ 390 ] = PR_mlock2, + [ 391 ] = PR_copy_file_range, + [ 392 ] = PR_preadv2, + [ 393 ] = PR_pwritev2, + [ 394 ] = PR_pkey_mprotect, + [ 395 ] = PR_pkey_alloc, + [ 396 ] = PR_pkey_free, + [ 397 ] = PR_statx, + [ 439 ] = PR_faccessat2, +}; diff --git a/app/src/main/cpp/proot/src/syscall/sysnums-arm64.h b/app/src/main/cpp/proot/src/syscall/sysnums-arm64.h new file mode 100644 index 0000000..2ddd59a --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/sysnums-arm64.h @@ -0,0 +1,282 @@ +#include "syscall/sysnum.h" + +static const Sysnum sysnums_arm64[] = { + [ 0 ] = PR_io_setup, + [ 1 ] = PR_io_destroy, + [ 2 ] = PR_io_submit, + [ 3 ] = PR_io_cancel, + [ 4 ] = PR_io_getevents, + [ 5 ] = PR_setxattr, + [ 6 ] = PR_lsetxattr, + [ 7 ] = PR_fsetxattr, + [ 8 ] = PR_getxattr, + [ 9 ] = PR_lgetxattr, + [ 10 ] = PR_fgetxattr, + [ 11 ] = PR_listxattr, + [ 12 ] = PR_llistxattr, + [ 13 ] = PR_flistxattr, + [ 14 ] = PR_removexattr, + [ 15 ] = PR_lremovexattr, + [ 16 ] = PR_fremovexattr, + [ 17 ] = PR_getcwd, + [ 18 ] = PR_lookup_dcookie, + [ 19 ] = PR_eventfd2, + [ 20 ] = PR_epoll_create1, + [ 21 ] = PR_epoll_ctl, + [ 22 ] = PR_epoll_pwait, + [ 23 ] = PR_dup, + [ 24 ] = PR_dup3, + [ 25 ] = PR_fcntl, + [ 26 ] = PR_inotify_init1, + [ 27 ] = PR_inotify_add_watch, + [ 28 ] = PR_inotify_rm_watch, + [ 29 ] = PR_ioctl, + [ 30 ] = PR_ioprio_set, + [ 31 ] = PR_ioprio_get, + [ 32 ] = PR_flock, + [ 33 ] = PR_mknodat, + [ 34 ] = PR_mkdirat, + [ 35 ] = PR_unlinkat, + [ 36 ] = PR_symlinkat, + [ 37 ] = PR_linkat, + [ 38 ] = PR_renameat, + [ 39 ] = PR_umount2, + [ 40 ] = PR_mount, + [ 41 ] = PR_pivot_root, + [ 42 ] = PR_nfsservctl, + [ 43 ] = PR_statfs, + [ 44 ] = PR_fstatfs, + [ 45 ] = PR_truncate, + [ 46 ] = PR_ftruncate, + [ 47 ] = PR_fallocate, + [ 48 ] = PR_faccessat, + [ 49 ] = PR_chdir, + [ 50 ] = PR_fchdir, + [ 51 ] = PR_chroot, + [ 52 ] = PR_fchmod, + [ 53 ] = PR_fchmodat, + [ 54 ] = PR_fchownat, + [ 55 ] = PR_fchown, + [ 56 ] = PR_openat, + [ 57 ] = PR_close, + [ 58 ] = PR_vhangup, + [ 59 ] = PR_pipe2, + [ 60 ] = PR_quotactl, + [ 61 ] = PR_getdents64, + [ 62 ] = PR_lseek, + [ 63 ] = PR_read, + [ 64 ] = PR_write, + [ 65 ] = PR_readv, + [ 66 ] = PR_writev, + [ 67 ] = PR_pread64, + [ 68 ] = PR_pwrite64, + [ 69 ] = PR_preadv, + [ 70 ] = PR_pwritev, + [ 71 ] = PR_sendfile, + [ 72 ] = PR_pselect6, + [ 73 ] = PR_ppoll, + [ 74 ] = PR_signalfd4, + [ 75 ] = PR_vmsplice, + [ 76 ] = PR_splice, + [ 77 ] = PR_tee, + [ 78 ] = PR_readlinkat, + [ 79 ] = PR_fstatat64, + [ 80 ] = PR_fstat, + [ 81 ] = PR_sync, + [ 82 ] = PR_fsync, + [ 83 ] = PR_fdatasync, + [ 84 ] = PR_sync_file_range, + [ 85 ] = PR_timerfd_create, + [ 86 ] = PR_timerfd_settime, + [ 87 ] = PR_timerfd_gettime, + [ 88 ] = PR_utimensat, + [ 89 ] = PR_acct, + [ 90 ] = PR_capget, + [ 91 ] = PR_capset, + [ 92 ] = PR_personality, + [ 93 ] = PR_exit, + [ 94 ] = PR_exit_group, + [ 95 ] = PR_waitid, + [ 96 ] = PR_set_tid_address, + [ 97 ] = PR_unshare, + [ 98 ] = PR_futex, + [ 99 ] = PR_set_robust_list, + [ 100 ] = PR_get_robust_list, + [ 101 ] = PR_nanosleep, + [ 102 ] = PR_getitimer, + [ 103 ] = PR_setitimer, + [ 104 ] = PR_kexec_load, + [ 105 ] = PR_init_module, + [ 106 ] = PR_delete_module, + [ 107 ] = PR_timer_create, + [ 108 ] = PR_timer_gettime, + [ 109 ] = PR_timer_getoverrun, + [ 110 ] = PR_timer_settime, + [ 111 ] = PR_timer_delete, + [ 112 ] = PR_clock_settime, + [ 113 ] = PR_clock_gettime, + [ 114 ] = PR_clock_getres, + [ 115 ] = PR_clock_nanosleep, + [ 116 ] = PR_syslog, + [ 117 ] = PR_ptrace, + [ 118 ] = PR_sched_setparam, + [ 119 ] = PR_sched_setscheduler, + [ 120 ] = PR_sched_getscheduler, + [ 121 ] = PR_sched_getparam, + [ 122 ] = PR_sched_setaffinity, + [ 123 ] = PR_sched_getaffinity, + [ 124 ] = PR_sched_yield, + [ 125 ] = PR_sched_get_priority_max, + [ 126 ] = PR_sched_get_priority_min, + [ 127 ] = PR_sched_rr_get_interval, + [ 128 ] = PR_restart_syscall, + [ 129 ] = PR_kill, + [ 130 ] = PR_tkill, + [ 131 ] = PR_tgkill, + [ 132 ] = PR_sigaltstack, + [ 133 ] = PR_rt_sigsuspend, + [ 134 ] = PR_rt_sigaction, + [ 135 ] = PR_rt_sigprocmask, + [ 136 ] = PR_rt_sigpending, + [ 137 ] = PR_rt_sigtimedwait, + [ 138 ] = PR_rt_sigqueueinfo, + [ 139 ] = PR_rt_sigreturn, + [ 140 ] = PR_setpriority, + [ 141 ] = PR_getpriority, + [ 142 ] = PR_reboot, + [ 143 ] = PR_setregid, + [ 144 ] = PR_setgid, + [ 145 ] = PR_setreuid, + [ 146 ] = PR_setuid, + [ 147 ] = PR_setresuid, + [ 148 ] = PR_getresuid, + [ 149 ] = PR_setresgid, + [ 150 ] = PR_getresgid, + [ 151 ] = PR_setfsuid, + [ 152 ] = PR_setfsgid, + [ 153 ] = PR_times, + [ 154 ] = PR_setpgid, + [ 155 ] = PR_getpgid, + [ 156 ] = PR_getsid, + [ 157 ] = PR_setsid, + [ 158 ] = PR_getgroups, + [ 159 ] = PR_setgroups, + [ 160 ] = PR_uname, + [ 161 ] = PR_sethostname, + [ 162 ] = PR_setdomainname, + [ 163 ] = PR_getrlimit, + [ 164 ] = PR_setrlimit, + [ 165 ] = PR_getrusage, + [ 166 ] = PR_umask, + [ 167 ] = PR_prctl, + [ 168 ] = PR_getcpu, + [ 169 ] = PR_gettimeofday, + [ 170 ] = PR_settimeofday, + [ 171 ] = PR_adjtimex, + [ 172 ] = PR_getpid, + [ 173 ] = PR_getppid, + [ 174 ] = PR_getuid, + [ 175 ] = PR_geteuid, + [ 176 ] = PR_getgid, + [ 177 ] = PR_getegid, + [ 178 ] = PR_gettid, + [ 179 ] = PR_sysinfo, + [ 180 ] = PR_mq_open, + [ 181 ] = PR_mq_unlink, + [ 182 ] = PR_mq_timedsend, + [ 183 ] = PR_mq_timedreceive, + [ 184 ] = PR_mq_notify, + [ 185 ] = PR_mq_getsetattr, + [ 186 ] = PR_msgget, + [ 187 ] = PR_msgctl, + [ 188 ] = PR_msgrcv, + [ 189 ] = PR_msgsnd, + [ 190 ] = PR_semget, + [ 191 ] = PR_semctl, + [ 192 ] = PR_semtimedop, + [ 193 ] = PR_semop, + [ 194 ] = PR_shmget, + [ 195 ] = PR_shmctl, + [ 196 ] = PR_shmat, + [ 197 ] = PR_shmdt, + [ 198 ] = PR_socket, + [ 199 ] = PR_socketpair, + [ 200 ] = PR_bind, + [ 201 ] = PR_listen, + [ 202 ] = PR_accept, + [ 203 ] = PR_connect, + [ 204 ] = PR_getsockname, + [ 205 ] = PR_getpeername, + [ 206 ] = PR_sendto, + [ 207 ] = PR_recvfrom, + [ 208 ] = PR_setsockopt, + [ 209 ] = PR_getsockopt, + [ 210 ] = PR_shutdown, + [ 211 ] = PR_sendmsg, + [ 212 ] = PR_recvmsg, + [ 213 ] = PR_readahead, + [ 214 ] = PR_brk, + [ 215 ] = PR_munmap, + [ 216 ] = PR_mremap, + [ 217 ] = PR_add_key, + [ 218 ] = PR_request_key, + [ 219 ] = PR_keyctl, + [ 220 ] = PR_clone, + [ 221 ] = PR_execve, + [ 222 ] = PR_mmap, + [ 223 ] = PR_fadvise64, + [ 224 ] = PR_swapon, + [ 225 ] = PR_swapoff, + [ 226 ] = PR_mprotect, + [ 227 ] = PR_msync, + [ 228 ] = PR_mlock, + [ 229 ] = PR_munlock, + [ 230 ] = PR_mlockall, + [ 231 ] = PR_munlockall, + [ 232 ] = PR_mincore, + [ 233 ] = PR_madvise, + [ 234 ] = PR_remap_file_pages, + [ 235 ] = PR_mbind, + [ 236 ] = PR_get_mempolicy, + [ 237 ] = PR_set_mempolicy, + [ 238 ] = PR_migrate_pages, + [ 239 ] = PR_move_pages, + [ 240 ] = PR_rt_tgsigqueueinfo, + [ 241 ] = PR_perf_event_open, + [ 242 ] = PR_accept4, + [ 243 ] = PR_recvmmsg, + [ 244 ] = PR_arch_specific_syscall, + [ 260 ] = PR_wait4, + [ 261 ] = PR_prlimit64, + [ 262 ] = PR_fanotify_init, + [ 263 ] = PR_fanotify_mark, + [ 264 ] = PR_name_to_handle_at, + [ 265 ] = PR_open_by_handle_at, + [ 266 ] = PR_clock_adjtime, + [ 267 ] = PR_syncfs, + [ 268 ] = PR_setns, + [ 269 ] = PR_sendmmsg, + [ 270 ] = PR_process_vm_readv, + [ 271 ] = PR_process_vm_writev, + [ 272 ] = PR_kcmp, + [ 273 ] = PR_finit_module, + [ 274 ] = PR_sched_setattr, + [ 275 ] = PR_sched_getattr, + [ 276 ] = PR_renameat2, + [ 277 ] = PR_seccomp, + [ 278 ] = PR_getrandom, + [ 279 ] = PR_memfd_create, + [ 280 ] = PR_bpf, + [ 281 ] = PR_execveat, + [ 282 ] = PR_userfaultfd, + [ 283 ] = PR_membarrier, + [ 284 ] = PR_mlock2, + [ 285 ] = PR_copy_file_range, + [ 286 ] = PR_preadv2, + [ 287 ] = PR_pwritev2, + [ 288 ] = PR_pkey_mprotect, + [ 289 ] = PR_pkey_alloc, + [ 290 ] = PR_pkey_free, + [ 291 ] = PR_statx, + [ 439 ] = PR_faccessat2, +}; diff --git a/app/src/main/cpp/proot/src/syscall/sysnums.list b/app/src/main/cpp/proot/src/syscall/sysnums.list new file mode 100644 index 0000000..5e9c0c9 --- /dev/null +++ b/app/src/main/cpp/proot/src/syscall/sysnums.list @@ -0,0 +1,447 @@ +SYSNUM(ARM_BASE) +SYSNUM(ARM_breakpoint) +SYSNUM(ARM_cacheflush) +SYSNUM(ARM_set_tls) +SYSNUM(ARM_usr26) +SYSNUM(ARM_usr32) +SYSNUM(X32_SYSCALL_BIT) +SYSNUM(_llseek) +SYSNUM(_newselect) +SYSNUM(_sysctl) +SYSNUM(accept) +SYSNUM(accept4) +SYSNUM(access) +SYSNUM(acct) +SYSNUM(add_key) +SYSNUM(adjtimex) +SYSNUM(afs_syscall) +SYSNUM(alarm) +SYSNUM(arch_prctl) +SYSNUM(arch_specific_syscall) +SYSNUM(arm_fadvise64_64) +SYSNUM(arm_sync_file_range) +SYSNUM(bdflush) +SYSNUM(bind) +SYSNUM(bpf) +SYSNUM(break) +SYSNUM(brk) +SYSNUM(cacheflush) +SYSNUM(capget) +SYSNUM(capset) +SYSNUM(chdir) +SYSNUM(chmod) +SYSNUM(chown) +SYSNUM(chown32) +SYSNUM(chroot) +SYSNUM(clock_adjtime) +SYSNUM(clock_getres) +SYSNUM(clock_gettime) +SYSNUM(clock_nanosleep) +SYSNUM(clock_settime) +SYSNUM(clone) +SYSNUM(close) +SYSNUM(connect) +SYSNUM(copy_file_range) +SYSNUM(creat) +SYSNUM(create_module) +SYSNUM(delete_module) +SYSNUM(dup) +SYSNUM(dup2) +SYSNUM(dup3) +SYSNUM(epoll_create) +SYSNUM(epoll_create1) +SYSNUM(epoll_ctl) +SYSNUM(epoll_ctl_old) +SYSNUM(epoll_pwait) +SYSNUM(epoll_wait) +SYSNUM(epoll_wait_old) +SYSNUM(eventfd) +SYSNUM(eventfd2) +SYSNUM(execve) +SYSNUM(execveat) +SYSNUM(exit) +SYSNUM(exit_group) +SYSNUM(faccessat) +SYSNUM(faccessat2) +SYSNUM(fadvise64) +SYSNUM(fadvise64_64) +SYSNUM(fallocate) +SYSNUM(fanotify_init) +SYSNUM(fanotify_mark) +SYSNUM(fchdir) +SYSNUM(fchmod) +SYSNUM(fchmodat) +SYSNUM(fchown) +SYSNUM(fchown32) +SYSNUM(fchownat) +SYSNUM(fcntl) +SYSNUM(fcntl64) +SYSNUM(fdatasync) +SYSNUM(fgetxattr) +SYSNUM(finit_module) +SYSNUM(flistxattr) +SYSNUM(flock) +SYSNUM(fork) +SYSNUM(fremovexattr) +SYSNUM(fsetxattr) +SYSNUM(fstat) +SYSNUM(fstat64) +SYSNUM(fstatat64) +SYSNUM(fstatfs) +SYSNUM(fstatfs64) +SYSNUM(fsync) +SYSNUM(ftime) +SYSNUM(ftruncate) +SYSNUM(ftruncate64) +SYSNUM(futex) +SYSNUM(futimesat) +SYSNUM(get_kernel_syms) +SYSNUM(get_mempolicy) +SYSNUM(get_robust_list) +SYSNUM(get_thread_area) +SYSNUM(getcpu) +SYSNUM(getcwd) +SYSNUM(getdents) +SYSNUM(getdents64) +SYSNUM(getegid) +SYSNUM(getegid32) +SYSNUM(geteuid) +SYSNUM(geteuid32) +SYSNUM(getgid) +SYSNUM(getgid32) +SYSNUM(getgroups) +SYSNUM(getgroups32) +SYSNUM(getitimer) +SYSNUM(getpeername) +SYSNUM(getpgid) +SYSNUM(getpgrp) +SYSNUM(getpid) +SYSNUM(getpmsg) +SYSNUM(getppid) +SYSNUM(getpriority) +SYSNUM(getrandom) +SYSNUM(getresgid) +SYSNUM(getresgid32) +SYSNUM(getresuid) +SYSNUM(getresuid32) +SYSNUM(getrlimit) +SYSNUM(getrusage) +SYSNUM(getsid) +SYSNUM(getsockname) +SYSNUM(getsockopt) +SYSNUM(gettid) +SYSNUM(gettimeofday) +SYSNUM(getuid) +SYSNUM(getuid32) +SYSNUM(getxattr) +SYSNUM(gtty) +SYSNUM(idle) +SYSNUM(init_module) +SYSNUM(inotify_add_watch) +SYSNUM(inotify_init) +SYSNUM(inotify_init1) +SYSNUM(inotify_rm_watch) +SYSNUM(io_cancel) +SYSNUM(io_destroy) +SYSNUM(io_getevents) +SYSNUM(io_setup) +SYSNUM(io_submit) +SYSNUM(ioctl) +SYSNUM(ioperm) +SYSNUM(iopl) +SYSNUM(ioprio_get) +SYSNUM(ioprio_set) +SYSNUM(ipc) +SYSNUM(kcmp) +SYSNUM(kexec_file_load) +SYSNUM(kexec_load) +SYSNUM(keyctl) +SYSNUM(kill) +SYSNUM(lchown) +SYSNUM(lchown32) +SYSNUM(lgetxattr) +SYSNUM(link) +SYSNUM(linkat) +SYSNUM(listen) +SYSNUM(listxattr) +SYSNUM(llistxattr) +SYSNUM(lock) +SYSNUM(lookup_dcookie) +SYSNUM(lremovexattr) +SYSNUM(lseek) +SYSNUM(lsetxattr) +SYSNUM(lstat) +SYSNUM(lstat64) +SYSNUM(madvise) +SYSNUM(mbind) +SYSNUM(membarrier) +SYSNUM(memfd_create) +SYSNUM(migrate_pages) +SYSNUM(mincore) +SYSNUM(mkdir) +SYSNUM(mkdirat) +SYSNUM(mknod) +SYSNUM(mknodat) +SYSNUM(mlock) +SYSNUM(mlock2) +SYSNUM(mlockall) +SYSNUM(mmap) +SYSNUM(mmap2) +SYSNUM(modify_ldt) +SYSNUM(mount) +SYSNUM(move_pages) +SYSNUM(mprotect) +SYSNUM(mpx) +SYSNUM(mq_getsetattr) +SYSNUM(mq_notify) +SYSNUM(mq_open) +SYSNUM(mq_timedreceive) +SYSNUM(mq_timedsend) +SYSNUM(mq_unlink) +SYSNUM(mremap) +SYSNUM(msgctl) +SYSNUM(msgget) +SYSNUM(msgrcv) +SYSNUM(msgsnd) +SYSNUM(msync) +SYSNUM(munlock) +SYSNUM(munlockall) +SYSNUM(munmap) +SYSNUM(name_to_handle_at) +SYSNUM(nanosleep) +SYSNUM(newfstatat) +SYSNUM(nfsservctl) +SYSNUM(nice) +SYSNUM(oldfstat) +SYSNUM(oldlstat) +SYSNUM(oldolduname) +SYSNUM(oldstat) +SYSNUM(olduname) +SYSNUM(open) +SYSNUM(open_by_handle_at) +SYSNUM(openat) +SYSNUM(pause) +SYSNUM(pciconfig_iobase) +SYSNUM(pciconfig_read) +SYSNUM(pciconfig_write) +SYSNUM(perf_event_open) +SYSNUM(personality) +SYSNUM(pipe) +SYSNUM(pipe2) +SYSNUM(pivot_root) +SYSNUM(pkey_alloc) +SYSNUM(pkey_free) +SYSNUM(pkey_mprotect) +SYSNUM(poll) +SYSNUM(ppoll) +SYSNUM(prctl) +SYSNUM(pread64) +SYSNUM(preadv) +SYSNUM(preadv2) +SYSNUM(prlimit64) +SYSNUM(process_vm_readv) +SYSNUM(process_vm_writev) +SYSNUM(prof) +SYSNUM(profil) +SYSNUM(pselect6) +SYSNUM(ptrace) +SYSNUM(putpmsg) +SYSNUM(pwrite64) +SYSNUM(pwritev) +SYSNUM(pwritev2) +SYSNUM(query_module) +SYSNUM(quotactl) +SYSNUM(read) +SYSNUM(readahead) +SYSNUM(readdir) +SYSNUM(readlink) +SYSNUM(readlinkat) +SYSNUM(readv) +SYSNUM(reboot) +SYSNUM(recv) +SYSNUM(recvfrom) +SYSNUM(recvmmsg) +SYSNUM(recvmsg) +SYSNUM(remap_file_pages) +SYSNUM(removexattr) +SYSNUM(rename) +SYSNUM(renameat) +SYSNUM(renameat2) +SYSNUM(request_key) +SYSNUM(restart_syscall) +SYSNUM(rmdir) +SYSNUM(rt_sigaction) +SYSNUM(rt_sigpending) +SYSNUM(rt_sigprocmask) +SYSNUM(rt_sigqueueinfo) +SYSNUM(rt_sigreturn) +SYSNUM(rt_sigsuspend) +SYSNUM(rt_sigtimedwait) +SYSNUM(rt_tgsigqueueinfo) +SYSNUM(sched_get_priority_max) +SYSNUM(sched_get_priority_min) +SYSNUM(sched_getaffinity) +SYSNUM(sched_getattr) +SYSNUM(sched_getparam) +SYSNUM(sched_getscheduler) +SYSNUM(sched_rr_get_interval) +SYSNUM(sched_setaffinity) +SYSNUM(sched_setattr) +SYSNUM(sched_setparam) +SYSNUM(sched_setscheduler) +SYSNUM(sched_yield) +SYSNUM(seccomp) +SYSNUM(security) +SYSNUM(select) +SYSNUM(semctl) +SYSNUM(semget) +SYSNUM(semop) +SYSNUM(semtimedop) +SYSNUM(send) +SYSNUM(sendfile) +SYSNUM(sendfile64) +SYSNUM(sendmmsg) +SYSNUM(sendmsg) +SYSNUM(sendto) +SYSNUM(set_mempolicy) +SYSNUM(set_robust_list) +SYSNUM(set_thread_area) +SYSNUM(set_tid_address) +SYSNUM(setdomainname) +SYSNUM(setfsgid) +SYSNUM(setfsgid32) +SYSNUM(setfsuid) +SYSNUM(setfsuid32) +SYSNUM(setgid) +SYSNUM(setgid32) +SYSNUM(setgroups) +SYSNUM(setgroups32) +SYSNUM(sethostname) +SYSNUM(setitimer) +SYSNUM(setns) +SYSNUM(setpgid) +SYSNUM(setpriority) +SYSNUM(setregid) +SYSNUM(setregid32) +SYSNUM(setresgid) +SYSNUM(setresgid32) +SYSNUM(setresuid) +SYSNUM(setresuid32) +SYSNUM(setreuid) +SYSNUM(setreuid32) +SYSNUM(setrlimit) +SYSNUM(setsid) +SYSNUM(setsockopt) +SYSNUM(settimeofday) +SYSNUM(setuid) +SYSNUM(setuid32) +SYSNUM(setxattr) +SYSNUM(sgetmask) +SYSNUM(shmat) +SYSNUM(shmctl) +SYSNUM(shmdt) +SYSNUM(shmget) +SYSNUM(shutdown) +SYSNUM(sigaction) +SYSNUM(sigaltstack) +SYSNUM(signal) +SYSNUM(signalfd) +SYSNUM(signalfd4) +SYSNUM(sigpending) +SYSNUM(sigprocmask) +SYSNUM(sigreturn) +SYSNUM(sigsuspend) +SYSNUM(socket) +SYSNUM(socketcall) +SYSNUM(socketpair) +SYSNUM(splice) +SYSNUM(ssetmask) +SYSNUM(stat) +SYSNUM(stat64) +SYSNUM(statfs) +SYSNUM(statfs64) +SYSNUM(statx) +SYSNUM(stime) +SYSNUM(stty) +SYSNUM(swapoff) +SYSNUM(swapon) +SYSNUM(symlink) +SYSNUM(symlinkat) +SYSNUM(sync) +SYSNUM(sync_file_range) +SYSNUM(sync_file_range2) +SYSNUM(syncfs) +SYSNUM(sysfs) +SYSNUM(sysinfo) +SYSNUM(syslog) +SYSNUM(tee) +SYSNUM(tgkill) +SYSNUM(time) +SYSNUM(timer_create) +SYSNUM(timer_delete) +SYSNUM(timer_getoverrun) +SYSNUM(timer_gettime) +SYSNUM(timer_settime) +SYSNUM(timerfd_create) +SYSNUM(timerfd_gettime) +SYSNUM(timerfd_settime) +SYSNUM(times) +SYSNUM(tkill) +SYSNUM(truncate) +SYSNUM(truncate64) +SYSNUM(tuxcall) +SYSNUM(ugetrlimit) +SYSNUM(ulimit) +SYSNUM(umask) +SYSNUM(umount) +SYSNUM(umount2) +SYSNUM(uname) +SYSNUM(unlink) +SYSNUM(unlinkat) +SYSNUM(unshare) +SYSNUM(uselib) +SYSNUM(userfaultfd) +SYSNUM(ustat) +SYSNUM(utime) +SYSNUM(utimensat) +SYSNUM(utimes) +SYSNUM(vfork) +SYSNUM(vhangup) +SYSNUM(vm86) +SYSNUM(vm86old) +SYSNUM(vmsplice) +SYSNUM(vserver) +SYSNUM(wait4) +SYSNUM(waitid) +SYSNUM(waitpid) +SYSNUM(write) +SYSNUM(writev) +SYSNUM(x32_execve) +SYSNUM(x32_get_robust_list) +SYSNUM(x32_ioctl) +SYSNUM(x32_kexec_load) +SYSNUM(x32_move_pages) +SYSNUM(x32_mq_notify) +SYSNUM(x32_preadv) +SYSNUM(x32_process_vm_readv) +SYSNUM(x32_process_vm_writev) +SYSNUM(x32_ptrace) +SYSNUM(x32_pwritev) +SYSNUM(x32_readv) +SYSNUM(x32_recvfrom) +SYSNUM(x32_recvmmsg) +SYSNUM(x32_recvmsg) +SYSNUM(x32_rt_sigaction) +SYSNUM(x32_rt_sigpending) +SYSNUM(x32_rt_sigqueueinfo) +SYSNUM(x32_rt_sigreturn) +SYSNUM(x32_rt_sigtimedwait) +SYSNUM(x32_rt_tgsigqueueinfo) +SYSNUM(x32_sendmmsg) +SYSNUM(x32_sendmsg) +SYSNUM(x32_set_robust_list) +SYSNUM(x32_sigaltstack) +SYSNUM(x32_timer_create) +SYSNUM(x32_vmsplice) +SYSNUM(x32_waitid) +SYSNUM(x32_writev) diff --git a/app/src/main/cpp/proot/src/tracee/abi.h b/app/src/main/cpp/proot/src/tracee/abi.h new file mode 100644 index 0000000..eadc85a --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/abi.h @@ -0,0 +1,82 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef TRACEE_ABI_H +#define TRACEE_ABI_H + +#include +#include /* offsetof(), */ +#include + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "arch.h" + +#include "attribute.h" + +typedef enum { + ABI_DEFAULT = 0, + ABI_2, /* ARM EABI on AArch64. */ + NB_MAX_ABIS, +} Abi; + +/** + * Return the ABI currently used by the given @tracee. + */ +#if defined(ARCH_ARM64) +static inline Abi get_abi(const Tracee *tracee) +{ + return tracee->is_aarch32 ? ABI_2 : ABI_DEFAULT; +} + +/** + * Return true if @tracee is a 32-bit process running on a 64-bit + * kernel. + */ +static inline bool is_32on64_mode(const Tracee *tracee) +{ + return tracee->is_aarch32; +} +#else +static inline Abi get_abi(const Tracee *tracee UNUSED) +{ + return ABI_DEFAULT; +} + +static inline bool is_32on64_mode(const Tracee *tracee UNUSED) +{ + return false; +} +#endif + +/** + * Return the size of a word according to the ABI currently used by + * the given @tracee. + */ +static inline size_t sizeof_word(const Tracee *tracee) +{ + return (is_32on64_mode(tracee) + ? sizeof(word_t) / 2 + : sizeof(word_t)); +} + +#endif /* TRACEE_ABI_H */ diff --git a/app/src/main/cpp/proot/src/tracee/event.c b/app/src/main/cpp/proot/src/tracee/event.c new file mode 100644 index 0000000..23a84c0 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/event.c @@ -0,0 +1,671 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* CLONE_*, */ +#include /* pid_t, */ +#include /* ptrace(1), PTRACE_*, */ +#include /* waitpid(2), */ +#include /* waitpid(2), */ +#include /* uname(2), */ +#include /* fork(2), chdir(2), getpid(2), */ +#include /* strcmp(3), */ +#include /* errno(3), */ +#include /* bool, true, false, */ +#include /* assert(3), */ +#include /* atexit(3), getenv(3), */ +#include /* talloc_*, */ +#include /* PRI*, */ +#include /* bzero(3), */ + +#include "tracee/event.h" +#include "tracee/seccomp.h" +#include "tracee/mem.h" +#include "cli/note.h" +#include "path/path.h" +#include "path/binding.h" +#include "syscall/syscall.h" +#include "syscall/seccomp.h" +#include "ptrace/wait.h" +#include "execve/elf.h" + +#include "attribute.h" +#include "compat.h" + +static bool seccomp_after_ptrace_enter = false; + +/** + * Start @tracee->exe with the given @argv[]. This function + * returns -errno if an error occurred, otherwise 0. + */ +int launch_process(Tracee *tracee, char *const argv[]) +{ + char *const default_argv[] = { "-sh", NULL }; + long status; + pid_t pid; + + /* Warn about open file descriptors. They won't be + * translated until they are closed. */ + if (tracee->verbose > 0) + list_open_fd(tracee); + + pid = fork(); + switch(pid) { + case -1: + note(tracee, ERROR, SYSTEM, "fork()"); + return -errno; + + case 0: /* child */ + /* Declare myself as ptraceable before executing the + * requested program. */ + status = ptrace(PTRACE_TRACEME, 0, NULL, NULL); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "ptrace(TRACEME)"); + return -errno; + } + + /* Synchronize with the tracer's event loop. Without + * this trick the tracer only sees the "return" from + * the next execve(2) so PRoot wouldn't handle the + * interpreter/runner. I also verified that strace + * does the same thing. */ + kill(getpid(), SIGSTOP); + + /* Improve performance by using seccomp mode 2 */ + enable_syscall_filtering(tracee); + + /* Now process is ptraced, so the current rootfs is already the + * guest rootfs. Note: Valgrind can't handle execve(2) on + * "foreign" binaries (ENOEXEC) but can handle execvp(3) on such + * binaries. */ + execvp(tracee->exe, argv[0] != NULL ? argv : default_argv); + return -errno; + + default: /* parent */ + /* We know the pid of the first tracee now. */ + tracee->pid = pid; + return 0; + } + + /* Never reached. */ + return -ENOSYS; +} + +/* Send the KILL signal to all tracees when PRoot has received a fatal + * signal. */ +static void kill_all_tracees2(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED) +{ + note(NULL, WARNING, INTERNAL, "signal %d received from process %d", + signum, siginfo->si_pid); + kill_all_tracees(); + + /* Exit immediately for system signals (segmentation fault, + * illegal instruction, ...), otherwise exit cleanly through + * the event loop. */ + if (signum != SIGQUIT) + _exit(EXIT_FAILURE); +} + +/** + * Helper for print_talloc_hierarchy(). + */ +static void print_talloc_chunk(const void *ptr, int depth, int max_depth UNUSED, + int is_ref, void *data UNUSED) +{ + const char *name; + size_t count; + size_t size; + + name = talloc_get_name(ptr); + size = talloc_get_size(ptr); + count = talloc_reference_count(ptr); + + if (depth == 0) + return; + + while (depth-- > 1) + fprintf(stderr, "\t"); + + fprintf(stderr, "%-16s ", name); + + if (is_ref) + fprintf(stderr, "-> %-8p", ptr); + else { + fprintf(stderr, "%-8p %zd bytes %zd ref'", ptr, size, count); + + if (name[0] == '$') { + fprintf(stderr, "\t(\"%s\")", (char *)ptr); + } + if (name[0] == '@') { + char **argv; + int i; + + fprintf(stderr, "\t("); + for (i = 0, argv = (char **)ptr; argv[i] != NULL; i++) + fprintf(stderr, "\"%s\", ", argv[i]); + fprintf(stderr, ")"); + } + else if (strcmp(name, "Tracee") == 0) { + fprintf(stderr, "\t(pid = %d, parent = %p)", + ((Tracee *)ptr)->pid, ((Tracee *)ptr)->parent); + } + else if (strcmp(name, "Bindings") == 0) { + Tracee *tracee; + + tracee = TRACEE(ptr); + + if (ptr == tracee->fs->bindings.pending) + fprintf(stderr, "\t(pending)"); + else if (ptr == tracee->fs->bindings.guest) + fprintf(stderr, "\t(guest)"); + else if (ptr == tracee->fs->bindings.host) + fprintf(stderr, "\t(host)"); + } + else if (strcmp(name, "Binding") == 0) { + Binding *binding = (Binding *)ptr; + fprintf(stderr, "\t(%s:%s)", binding->host.path, binding->guest.path); + } + } + + fprintf(stderr, "\n"); +} + +/* Print on stderr the complete talloc hierarchy. */ +static void print_talloc_hierarchy(int signum, siginfo_t *siginfo UNUSED, void *ucontext UNUSED) +{ + switch (signum) { + case SIGUSR1: + talloc_report_depth_cb(NULL, 0, 100, print_talloc_chunk, NULL); + break; + + case SIGUSR2: + talloc_report_depth_file(NULL, 0, 100, stderr); + break; + + default: + break; + } +} + +static int last_exit_status = -1; + +/** + * Wait then handle any event from any tracee. This function returns + * the exit status of the last terminated program. + */ +int event_loop() +{ + struct sigaction signal_action; + long status; + int signum; + + /* Kill all tracees when exiting. */ + status = atexit(kill_all_tracees); + if (status != 0) + note(NULL, WARNING, INTERNAL, "atexit() failed"); + + /* All signals are blocked when the signal handler is called. + * SIGINFO is used to know which process has signaled us and + * RESTART is used to restart waitpid(2) seamlessly. */ + bzero(&signal_action, sizeof(signal_action)); + signal_action.sa_flags = SA_SIGINFO | SA_RESTART; + status = sigfillset(&signal_action.sa_mask); + if (status < 0) + note(NULL, WARNING, SYSTEM, "sigfillset()"); + + /* Handle all signals. */ + for (signum = 0; signum < SIGRTMAX; signum++) { + switch (signum) { + case SIGQUIT: + case SIGILL: + case SIGABRT: + case SIGFPE: + case SIGSEGV: + /* Kill all tracees on abnormal termination + * signals. This ensures no process is left + * untraced. */ + signal_action.sa_sigaction = kill_all_tracees2; + break; + + case SIGUSR1: + case SIGUSR2: + /* Print on stderr the complete talloc + * hierarchy, useful for debug purpose. */ + signal_action.sa_sigaction = print_talloc_hierarchy; + break; + + case SIGCHLD: + case SIGCONT: + case SIGSTOP: + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + /* The default action is OK for these signals, + * they are related to tty and job control. */ + continue; + + default: + /* Ignore all other signals, including + * terminating ones (^C for instance). */ + signal_action.sa_sigaction = (void *)SIG_IGN; + break; + } + + status = sigaction(signum, &signal_action, NULL); + if (status < 0 && errno != EINVAL) + note(NULL, WARNING, SYSTEM, "sigaction(%d)", signum); + } + + while (1) { + int tracee_status; + Tracee *tracee; + int signal; + pid_t pid; + + /* This is the only safe place to free tracees. */ + free_terminated_tracees(); + + /* Wait for the next tracee's stop. */ + pid = waitpid(-1, &tracee_status, __WALL); + if (pid < 0) { + if (errno != ECHILD) { + note(NULL, ERROR, SYSTEM, "waitpid()"); + return EXIT_FAILURE; + } + break; + } + + /* Get information about this tracee. */ + tracee = get_tracee(NULL, pid, true); + assert(tracee != NULL); + + tracee->running = false; + + if (tracee->as_ptracee.ptracer != NULL) { + bool keep_stopped = handle_ptracee_event(tracee, tracee_status); + if (keep_stopped) + continue; + } + + signal = handle_tracee_event(tracee, tracee_status); + (void) restart_tracee(tracee, signal); + } + + return last_exit_status; +} + +/** + * Handle the current event (@tracee_status) of the given @tracee. + * This function returns the "computed" signal that should be used to + * restart the given @tracee. + */ +int handle_tracee_event(Tracee *tracee, int tracee_status) +{ + static bool seccomp_detected = false; + static bool seccomp_after_ptrace_enter_checked = false; + long status; + int signal; + bool sysexit_necessary; + + if (!seccomp_after_ptrace_enter_checked) { + seccomp_after_ptrace_enter = getenv("PROOT_ASSUME_NEW_SECCOMP") != NULL; + seccomp_after_ptrace_enter_checked = true; + } + + /* When seccomp is enabled, all events are restarted in + * non-stop mode, but this default choice could be overwritten + * later if necessary. The check against "sysexit_pending" + * ensures PTRACE_SYSCALL (used to hit the exit stage under + * seccomp) is not cleared due to an event that would happen + * before the exit stage, eg. PTRACE_EVENT_EXEC for the exit + * stage of execve(2). */ + sysexit_necessary = tracee->sysexit_pending + || tracee->chain.syscalls != NULL + || tracee->restore_original_regs_after_seccomp_event; + /* Don't overwrite restart_how if it is explicitly set + * elsewhere, i.e in the ptrace emulation when single + * stepping. */ + if (tracee->restart_how == 0) { + if (tracee->seccomp == ENABLED && !sysexit_necessary) + tracee->restart_how = PTRACE_CONT; + else + tracee->restart_how = PTRACE_SYSCALL; + } + + /* Not a signal-stop by default. */ + signal = 0; + + if (WIFEXITED(tracee_status)) { + last_exit_status = WEXITSTATUS(tracee_status); + VERBOSE(tracee, 1, "pid %d: exited with status %d", tracee->pid, last_exit_status); + terminate_tracee(tracee); + } + else if (WIFSIGNALED(tracee_status)) { + VERBOSE(tracee, (int) (tracee->pid != 1), "pid %d: terminated with signal %d", tracee->pid, WTERMSIG(tracee_status)); + terminate_tracee(tracee); + } + else if (WIFSTOPPED(tracee_status)) { + /* Don't use WSTOPSIG() to extract the signal + * since it clears the PTRACE_EVENT_* bits. */ + signal = (tracee_status & 0xfff00) >> 8; + + switch (signal) { + static bool deliver_sigtrap = false; + + case SIGTRAP: { + const unsigned long default_ptrace_options = ( + PTRACE_O_TRACESYSGOOD | + PTRACE_O_TRACEFORK | + PTRACE_O_TRACEVFORK | + PTRACE_O_TRACEVFORKDONE | + PTRACE_O_TRACEEXEC | + PTRACE_O_TRACECLONE | + PTRACE_O_TRACEEXIT); + + /* Distinguish some events from others and + * automatically trace each new process with + * the same options. + * + * Note that only the first bare SIGTRAP is + * related to the tracing loop, others SIGTRAP + * carry tracing information because of + * TRACE*FORK/CLONE/EXEC. */ + if (deliver_sigtrap) + break; /* Deliver this signal as-is. */ + + deliver_sigtrap = true; + + /* Try to enable seccomp mode 2... */ + status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL, + default_ptrace_options | PTRACE_O_TRACESECCOMP); + if (status < 0) { + /* ... otherwise use default options only. */ + status = ptrace(PTRACE_SETOPTIONS, tracee->pid, NULL, + default_ptrace_options); + if (status < 0) { + note(tracee, ERROR, SYSTEM, "ptrace(PTRACE_SETOPTIONS)"); + exit(EXIT_FAILURE); + } + } + } + /* Fall through. */ + case SIGTRAP | 0x80: + signal = 0; + + /* This tracee got signaled then freed during the + sysenter stage but the kernel reports the sysexit + stage; just discard this spurious tracee/event. */ + if (tracee->exe == NULL) { + tracee->restart_how = PTRACE_CONT; + return 0; + } + + switch (tracee->seccomp) { + case ENABLED: + if (IS_IN_SYSENTER(tracee)) { + /* sysenter: ensure the sysexit + * stage will be hit under seccomp. */ + tracee->restart_how = PTRACE_SYSCALL; + tracee->sysexit_pending = true; + } + else { + /* sysexit: the next sysenter + * will be notified by seccomp. */ + tracee->restart_how = PTRACE_CONT; + tracee->sysexit_pending = false; + } + /* Fall through. */ + case DISABLED: + if (!tracee->seccomp_already_handled_enter) + { + bool was_sysenter = IS_IN_SYSENTER(tracee); + + translate_syscall(tracee); + + /* In case we've changed on enter sysnum to PR_void, + * seccomp check is happening after our change + * and seccomp policy raises SIGSYS on -1 syscall, + * set tracee flag to ignore next SIGSYS. */ + if (was_sysenter) { + tracee->skip_next_seccomp_signal = ( + seccomp_after_ptrace_enter && + get_sysnum(tracee, CURRENT) == PR_void); + } + + /* Redeliver signal suppressed during + * syscall chain once it's finished. */ + if (tracee->chain.suppressed_signal && tracee->chain.syscalls == NULL) { + signal = tracee->chain.suppressed_signal; + tracee->chain.suppressed_signal = 0; + VERBOSE(tracee, 6, "pid %d: redelivering suppressed signal %d", tracee->pid, signal); + } + } + else { + VERBOSE(tracee, 6, "skipping SIGTRAP for already handled sysenter"); + assert(!IS_IN_SYSENTER(tracee)); + assert(!seccomp_after_ptrace_enter); + tracee->seccomp_already_handled_enter = false; + tracee->restart_how = PTRACE_SYSCALL; + } + + /* This syscall has disabled seccomp. */ + if (tracee->seccomp == DISABLING) { + tracee->restart_how = PTRACE_SYSCALL; + tracee->seccomp = DISABLED; + } + + break; + + case DISABLING: + /* Seccomp was disabled by the + * previous syscall, but its sysenter + * stage was already handled. */ + tracee->seccomp = DISABLED; + if (IS_IN_SYSENTER(tracee)) + tracee->status = 1; + break; + } + break; + + case SIGTRAP | PTRACE_EVENT_SECCOMP2 << 8: + case SIGTRAP | PTRACE_EVENT_SECCOMP << 8: { + unsigned long flags = 0; + + signal = 0; + + if (!seccomp_detected) { + tracee->seccomp = ENABLED; + seccomp_detected = true; + seccomp_after_ptrace_enter = !IS_IN_SYSENTER(tracee); + VERBOSE(tracee, 1, "ptrace acceleration (seccomp mode 2, %s syscall order) enabled", + seccomp_after_ptrace_enter ? "new" : "old"); + } + + tracee->skip_next_seccomp_signal = false; + + /* If kernel triggered seccomp event after we handled + * syscall enter, skip this event and continue as it didn't happen */ + if (seccomp_after_ptrace_enter && !IS_IN_SYSENTER(tracee)) + { + tracee->restart_how = tracee->last_restart_how; + VERBOSE(tracee, 6, "skipping PTRACE_EVENT_SECCOMP for already handled sysenter"); + + /* "!IS_IN_SYSENTER(tracee)" in condition above means we have pending sysexit, + * so requesting to not be notified about it makes no sense */ + assert(tracee->restart_how != PTRACE_CONT); + break; + } + + assert(IS_IN_SYSENTER(tracee)); + + /* Use the common ptrace flow if seccomp was + * explicitely disabled for this tracee. */ + if (tracee->seccomp != ENABLED) + break; + + status = ptrace(PTRACE_GETEVENTMSG, tracee->pid, NULL, &flags); + if (status < 0) + break; + + /* Use the common ptrace flow when + * sysexit has to be handled. */ + if ((flags & FILTER_SYSEXIT) != 0 || sysexit_necessary) { + if (seccomp_after_ptrace_enter) { + tracee->restart_how = PTRACE_SYSCALL; + translate_syscall(tracee); + } + tracee->restart_how = PTRACE_SYSCALL; + break; + } + + /* Otherwise, handle the sysenter + * stage right now. */ + tracee->restart_how = PTRACE_CONT; + translate_syscall(tracee); + + /* This syscall has disabled seccomp, so move + * the ptrace flow back to the common path to + * ensure its sysexit will be handled. */ + if (tracee->seccomp == DISABLING) + tracee->restart_how = PTRACE_SYSCALL; + + if (!seccomp_after_ptrace_enter && tracee->restart_how == PTRACE_SYSCALL) + tracee->seccomp_already_handled_enter = true; + break; + } + + case SIGTRAP | PTRACE_EVENT_VFORK << 8: + signal = 0; + (void) new_child(tracee, CLONE_VFORK); + break; + + case SIGTRAP | PTRACE_EVENT_FORK << 8: + case SIGTRAP | PTRACE_EVENT_CLONE << 8: + signal = 0; + (void) new_child(tracee, 0); + break; + + case SIGTRAP | PTRACE_EVENT_VFORK_DONE << 8: + case SIGTRAP | PTRACE_EVENT_EXEC << 8: + case SIGTRAP | PTRACE_EVENT_EXIT << 8: + signal = 0; + if (tracee->last_restart_how) { + tracee->restart_how = tracee->last_restart_how; + } + break; + + case SIGSTOP: + /* Stop this tracee until PRoot has received + * the EVENT_*FORK|CLONE notification. */ + if (tracee->exe == NULL) { + tracee->sigstop = SIGSTOP_PENDING; + signal = -1; + } + + /* For each tracee, the first SIGSTOP + * is only used to notify the tracer. */ + if (tracee->sigstop == SIGSTOP_IGNORED) { + tracee->sigstop = SIGSTOP_ALLOWED; + signal = 0; + } + break; + + case SIGSYS: { + siginfo_t siginfo = {}; + ptrace(PTRACE_GETSIGINFO, tracee->pid, NULL, &siginfo); + if (siginfo.si_code == SYS_SECCOMP) { + /* Signal cannot happen when we're inside syscall, + * tracee would have to exit from syscall first. + * Execute exit handler now if seccomp triggered sysexit skip. */ + if (!IS_IN_SYSENTER(tracee)) { + VERBOSE(tracee, 1, "Handling syscall exit from SIGSYS"); + translate_syscall(tracee); + } + + if (tracee->skip_next_seccomp_signal || (seccomp_after_ptrace_enter && siginfo.si_syscall == SYSCALL_AVOIDER)) { + VERBOSE(tracee, 4, "suppressed SIGSYS after void syscall"); + tracee->skip_next_seccomp_signal = false; + signal = 0; + } else { + signal = handle_seccomp_event(tracee); + } + } else { + VERBOSE(tracee, 1, "non-seccomp SIGSYS"); + } + break; + } + + default: + /* Deliver this signal as-is, + * unless we're chaining syscall. */ + if (tracee->chain.syscalls != NULL) { + VERBOSE(tracee, 5, + "pid %d: suppressing signal during chain signal=%d, prev suppressed_signal=%d", + tracee->pid, signal, tracee->chain.suppressed_signal); + tracee->chain.suppressed_signal = signal; + signal = 0; + } + break; + } + } + + /* Clear the pending event, if any. */ + tracee->as_ptracee.event4.proot.pending = false; + + return signal; +} + +/** + * Returns true if on current system SIGTRAP|0x80 + * for syscall enter is reported before SIGSYS + * when syscall is being blocked by seccomp + */ +bool seccomp_event_happens_after_enter_sigtrap() +{ + return !seccomp_after_ptrace_enter; +} + +/** + * Restart the given @tracee with the specified @signal. This + * function returns false if the tracee was not restarted (error or + * put in the "waiting for ptracee" state), otherwise true. + */ +bool restart_tracee(Tracee *tracee, int signal) +{ + int status; + + /* Put in the "stopped"/"waiting for ptracee" state?. */ + if (tracee->as_ptracer.wait_pid != 0 || signal == -1) + return false; + + /* Restart the tracee and stop it at the next instruction, or + * at the next entry or exit of a system call. */ + assert(tracee->restart_how != 0); + status = ptrace(tracee->restart_how, tracee->pid, NULL, signal); + if (status < 0) + return false; /* The process likely died in a syscall. */ + + tracee->last_restart_how = tracee->restart_how; + tracee->restart_how = 0; + tracee->running = true; + + return true; +} diff --git a/app/src/main/cpp/proot/src/tracee/event.h b/app/src/main/cpp/proot/src/tracee/event.h new file mode 100644 index 0000000..3f78988 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/event.h @@ -0,0 +1,36 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef TRACEE_EVENT_H +#define TRACEE_EVENT_H + +#include + +#include "tracee/tracee.h" + +extern int launch_process(Tracee *tracee, char *const argv[]); +extern int event_loop(); +extern int handle_tracee_event(Tracee *tracee, int tracee_status); +extern bool restart_tracee(Tracee *tracee, int signal); +extern bool seccomp_event_happens_after_enter_sigtrap(); + +#endif /* TRACEE_EVENT_H */ diff --git a/app/src/main/cpp/proot/src/tracee/mem.c b/app/src/main/cpp/proot/src/tracee/mem.c new file mode 100644 index 0000000..943f3b8 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/mem.c @@ -0,0 +1,376 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* ptrace(2), PTRACE_*, */ +#include /* pid_t, size_t, */ +#include /* NULL, */ +#include /* offsetof(), */ +#include /* struct user*, */ +#include /* errno, */ +#include /* assert(3), */ +#include /* waitpid(2), */ +#include /* memcpy(3), */ +#include /* uint*_t, */ +#include /* process_vm_*, struct iovec, */ +#include /* sysconf(3), */ +#include /* mmap(2), munmap(2), MAP_*, */ + +#include "tracee/mem.h" +#include "tracee/abi.h" +#include "syscall/heap.h" +#include "arch.h" /* word_t, NO_MISALIGNED_ACCESS */ +#include "cli/note.h" + +/** + * Load the word at the given @address, potentially *not* aligned. + */ +static inline word_t load_word(const void *address) +{ +#ifdef NO_MISALIGNED_ACCESS + if (((word_t)address) % sizeof(word_t) == 0) + return *(word_t *)address; + else { + word_t value; + memcpy(&value, address, sizeof(word_t)); + return value; + } +#else + return *(word_t *)address; +#endif +} + +/** + * Store the word with the given @value to the given @address, + * potentially *not* aligned. + */ +static inline void store_word(void *address, word_t value) +{ +#ifdef NO_MISALIGNED_ACCESS + if (((word_t)address) % sizeof(word_t) == 0) + *((word_t *)address) = value; + else + memcpy(address, &value, sizeof(word_t)); +#else + *((word_t *)address) = value; +#endif +} + +/** + * Copy @size bytes from the buffer @src_tracer to the address + * @dest_tracee within the memory space of the @tracee process. It + * returns -errno if an error occured, otherwise 0. + */ +int write_data(Tracee *tracee, word_t dest_tracee, const void *src_tracer, word_t size) +{ + word_t *src = (word_t *)src_tracer; + word_t *dest = (word_t *)dest_tracee; + + long status; + word_t word, i, j; + word_t nb_trailing_bytes; + word_t nb_full_words; + + uint8_t *last_dest_word; + uint8_t *last_src_word; + + nb_trailing_bytes = size % sizeof(word_t); + nb_full_words = (size - nb_trailing_bytes) / sizeof(word_t); + + /* Clear errno so we won't detect previous syscall failure as ptrace one */ + errno = 0; + + /* Copy one word by one word, except for the last one. */ + for (i = 0; i < nb_full_words; i++) { + status = ptrace(PTRACE_POKEDATA, tracee->pid, (word_t)(dest + i), load_word(&src[i])); + if (status < 0) { + note(tracee, WARNING, SYSTEM, "ptrace(POKEDATA)"); + return -EFAULT; + } + } + + if (nb_trailing_bytes == 0) + return 0; + + /* Copy the bytes in the last word carefully since we have to + * overwrite only the relevant ones. */ + + /* Clear errno so we won't detect previous syscall failure as ptrace one */ + errno = 0; + + word = ptrace(PTRACE_PEEKDATA, tracee->pid, dest + i, NULL); + if (errno != 0) { + note(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); + return -EFAULT; + } + + last_dest_word = (uint8_t *)&word; + last_src_word = (uint8_t *)&src[i]; + + for (j = 0; j < nb_trailing_bytes; j++) + last_dest_word[j] = last_src_word[j]; + + status = ptrace(PTRACE_POKEDATA, tracee->pid, (word_t)(dest + i), word); + if (status < 0) { + note(tracee, WARNING, SYSTEM, "ptrace(POKEDATA)"); + return -EFAULT; + } + + return 0; +} + +/** + * Gather the @src_tracer_count buffers pointed to by @src_tracer to + * the address @dest_tracee within the memory space of the @tracee + * process. This function returns -errno if an error occured, + * otherwise 0. + */ +int writev_data(Tracee *tracee, word_t dest_tracee, const struct iovec *src_tracer, int src_tracer_count) +{ + size_t size; + int status; + int i; + + for (i = 0, size = 0; i < src_tracer_count; i++) { + status = write_data(tracee, dest_tracee + size, + src_tracer[i].iov_base, src_tracer[i].iov_len); + if (status < 0) + return status; + + size += src_tracer[i].iov_len; + } + + return 0; +} + +/** + * Copy @size bytes to the buffer @dest_tracer from the address + * @src_tracee within the memory space of the @tracee process. It + * returns -errno if an error occured, otherwise 0. + */ +int read_data(const Tracee *tracee, void *dest_tracer, word_t src_tracee, word_t size) +{ + word_t *src = (word_t *)src_tracee; + word_t *dest = (word_t *)dest_tracer; + + word_t nb_trailing_bytes; + word_t nb_full_words; + word_t word, i, j; + + uint8_t *last_src_word; + uint8_t *last_dest_word; + + nb_trailing_bytes = size % sizeof(word_t); + nb_full_words = (size - nb_trailing_bytes) / sizeof(word_t); + + /* Clear errno so we won't detect previous syscall failure as ptrace one */ + errno = 0; + + /* Copy one word by one word, except for the last one. */ + for (i = 0; i < nb_full_words; i++) { + word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); + if (errno != 0) { + note(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); + return -EFAULT; + } + store_word(&dest[i], word); + } + + if (nb_trailing_bytes == 0) + return 0; + + /* Copy the bytes from the last word carefully since we have + * to not overwrite the bytes lying beyond @dest_tracer. */ + + word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); + if (errno != 0) { + note(tracee, WARNING, SYSTEM, "ptrace(PEEKDATA)"); + return -EFAULT; + } + + last_dest_word = (uint8_t *)&dest[i]; + last_src_word = (uint8_t *)&word; + + for (j = 0; j < nb_trailing_bytes; j++) + last_dest_word[j] = last_src_word[j]; + + return 0; +} + +/** + * Copy to @dest_tracer at most @max_size bytes from the string + * pointed to by @src_tracee within the memory space of the @tracee + * process. This function returns -errno on error, otherwise + * it returns the number in bytes of the string, including the + * end-of-string terminator. + */ +int read_string(const Tracee *tracee, char *dest_tracer, word_t src_tracee, word_t max_size) +{ + word_t *src = (word_t *)src_tracee; + word_t *dest = (word_t *)dest_tracer; + + word_t nb_trailing_bytes; + word_t nb_full_words; + word_t word, i, j; + + uint8_t *src_word; + uint8_t *dest_word; + + nb_trailing_bytes = max_size % sizeof(word_t); + nb_full_words = (max_size - nb_trailing_bytes) / sizeof(word_t); + + /* Clear errno so we won't detect previous syscall failure as ptrace one */ + errno = 0; + + /* Copy one word by one word, except for the last one. */ + for (i = 0; i < nb_full_words; i++) { + word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); + if (errno != 0) + return -EFAULT; + + store_word(&dest[i], word); + + /* Stop once an end-of-string is detected. */ + src_word = (uint8_t *)&word; + for (j = 0; j < sizeof(word_t); j++) + if (src_word[j] == '\0') + return i * sizeof(word_t) + j + 1; + } + + /* Copy the bytes from the last word carefully since we have + * to not overwrite the bytes lying beyond @dest_tracer. */ + + word = ptrace(PTRACE_PEEKDATA, tracee->pid, src + i, NULL); + if (errno != 0) + return -EFAULT; + + dest_word = (uint8_t *)&dest[i]; + src_word = (uint8_t *)&word; + + for (j = 0; j < nb_trailing_bytes; j++) { + dest_word[j] = src_word[j]; + if (src_word[j] == '\0') + break; + } + + return i * sizeof(word_t) + j + 1; +} + +/** + * Return the value of the word at the given @address in the @tracee's + * memory space. The caller must test errno to check if an error + * occured. + */ +word_t peek_word(const Tracee *tracee, word_t address) +{ + word_t result = 0; + + errno = 0; + result = (word_t) ptrace(PTRACE_PEEKDATA, tracee->pid, address, NULL); + + /* From ptrace(2) manual: "Unfortunately, under Linux, + * different variations of this fault will return EIO or + * EFAULT more or less arbitrarily." */ + if (errno == EIO) + errno = EFAULT; + + /* Use only the 32 LSB when running a 32-bit process on a + * 64-bit kernel. */ + if (is_32on64_mode(tracee)) + result &= 0xFFFFFFFF; + + return result; +} + +/** + * Set the word at the given @address in the @tracee's memory space to + * the given @value. The caller must test errno to check if an error + * occured. + */ +void poke_word(const Tracee *tracee, word_t address, word_t value) +{ + word_t tmp; + + /* Don't overwrite the 32 MSB when running a 32-bit process on + * a 64-bit kernel. */ + if (is_32on64_mode(tracee)) { + errno = 0; + tmp = (word_t) ptrace(PTRACE_PEEKDATA, tracee->pid, address, NULL); + if (errno != 0) + return; + + value |= (tmp & 0xFFFFFFFF00000000ULL); + } + + errno = 0; + (void) ptrace(PTRACE_POKEDATA, tracee->pid, address, value); + + /* From ptrace(2) manual: "Unfortunately, under Linux, + * different variations of this fault will return EIO or + * EFAULT more or less arbitrarily." */ + if (errno == EIO) + errno = EFAULT; + + return; +} + +/** + * Allocate @size bytes in the @tracee's memory space. This function + * returns the address of the allocated memory in the @tracee's memory + * space, otherwise 0 if an error occured. + */ +word_t alloc_mem(Tracee *tracee, ssize_t size) +{ + word_t stack_pointer; + + /* This function should be called in sysenter only since the + * stack pointer is systematically restored at the end of + * sysexit (except for execve, but in this case the stack + * pointer should be handled with care since it is used by the + * process to retrieve argc, argv, envp, and auxv). */ + assert(IS_IN_SYSENTER(tracee)); + + /* Get the current value of the stack pointer from the tracee's + * USER area. */ + stack_pointer = peek_reg(tracee, CURRENT, STACK_POINTER); + + /* Some ABIs specify an amount of bytes after the stack + * pointer that shall not be used by anything but the compiler + * (for optimization purpose). */ + if (stack_pointer == peek_reg(tracee, ORIGINAL, STACK_POINTER)) + size += RED_ZONE_SIZE; + + /* Sanity check. */ + if ( (size > 0 && stack_pointer <= (word_t) size) + || (size < 0 && stack_pointer >= ULONG_MAX + size)) { + note(tracee, WARNING, INTERNAL, "integer under/overflow detected in %s", + __FUNCTION__); + return 0; + } + + /* Remember the stack grows downward. */ + stack_pointer -= size; + + /* Set the new value of the stack pointer in the tracee's USER + * area. */ + poke_reg(tracee, STACK_POINTER, stack_pointer); + return stack_pointer; +} diff --git a/app/src/main/cpp/proot/src/tracee/mem.h b/app/src/main/cpp/proot/src/tracee/mem.h new file mode 100644 index 0000000..b611ab6 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/mem.h @@ -0,0 +1,111 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef TRACEE_MEM_H +#define TRACEE_MEM_H + +#include /* PATH_MAX, */ +#include /* pid_t, size_t, */ +#include /* pid_t, size_t, */ +#include /* struct iovec, */ +#include /* ENAMETOOLONG, */ + +#include "arch.h" /* word_t, */ +#include "tracee/tracee.h" + +extern int write_data(Tracee *tracee, word_t dest_tracee, const void *src_tracer, word_t size); +extern int writev_data(Tracee *tracee, word_t dest_tracee, const struct iovec *src_tracer, int src_tracer_count); +extern int read_data(const Tracee *tracee, void *dest_tracer, word_t src_tracee, word_t size); +extern int read_string(const Tracee *tracee, char *dest_tracer, word_t src_tracee, word_t max_size); +extern word_t peek_word(const Tracee *tracee, word_t address); +extern void poke_word(const Tracee *tracee, word_t address, word_t value); +extern word_t alloc_mem(Tracee *tracee, ssize_t size); + +/** + * Copy to @dest_tracer at most PATH_MAX bytes -- including the + * end-of-string terminator -- from the string pointed to by + * @src_tracee within the memory space of the @tracee process. This + * function returns -errno on error, otherwise it returns the number + * in bytes of the string, including the end-of-string terminator. + */ +static inline int read_path(const Tracee *tracee, char dest_tracer[PATH_MAX], word_t src_tracee) +{ + int status; + + status = read_string(tracee, dest_tracer, src_tracee, PATH_MAX); + if (status < 0) + return status; + if (status >= PATH_MAX) + return -ENAMETOOLONG; + + return status; +} + +/** + * Generate a function that returns the value of the @type at the + * given @address in the @tracee's memory space. The caller must test + * errno to check if an error occured. + */ +#define GENERATE_peek(type) \ +static inline type ## _t peek_ ## type(const Tracee *tracee, word_t address) \ +{ \ + type ## _t result; \ + errno = -read_data(tracee, &result, address, sizeof(type ## _t)); \ + return result; \ +} + +GENERATE_peek(uint8); +GENERATE_peek(uint16); +GENERATE_peek(uint32); +GENERATE_peek(uint64); + +GENERATE_peek(int8); +GENERATE_peek(int16); +GENERATE_peek(int32); +GENERATE_peek(int64); + +#undef GENERATE_peek + +/** + * Generate a function that set the @type at the given @address in the + * @tracee's memory space to the given @value. The caller must test + * errno to check if an error occured. + */ +#define GENERATE_poke(type) \ +static inline void poke_ ## type(Tracee *tracee, word_t address, type ## _t value) \ +{ \ + errno = -write_data(tracee, address, &value, sizeof(type ## _t)); \ +} + +GENERATE_poke(uint8); +GENERATE_poke(uint16); +GENERATE_poke(uint32); +GENERATE_poke(uint64); + +GENERATE_poke(int8); +GENERATE_poke(int16); +GENERATE_poke(int32); +GENERATE_poke(int64); + +#undef GENERATE_poke + +#endif /* TRACEE_MEM_H */ diff --git a/app/src/main/cpp/proot/src/tracee/reg.c b/app/src/main/cpp/proot/src/tracee/reg.c new file mode 100644 index 0000000..c37ddd5 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/reg.c @@ -0,0 +1,310 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* off_t */ +#include /* struct user*, */ +#include /* ptrace(2), PTRACE*, */ +#include /* assert(3), */ +#include /* errno(3), */ +#include /* offsetof(), */ +#include /* *int*_t, */ +#include /* PRI*, */ +#include /* ULONG_MAX, */ +#include /* memcpy(3), */ +#include /* struct iovec, */ + +#include "arch.h" + +#if defined(ARCH_ARM64) +#include /* NT_PRSTATUS */ +#endif + +#include "syscall/sysnum.h" +#include "tracee/reg.h" +#include "tracee/abi.h" +#include "cli/note.h" +#include "compat.h" + +/** + * Compute the offset of the register @reg_name in the USER area. + */ +#define USER_REGS_OFFSET(reg_name) \ + (offsetof(struct user, regs) \ + + offsetof(struct user_regs_struct, reg_name)) + +#define REG(tracee, version, index) \ + (*(word_t*) (((uint8_t *) &tracee->_regs[version]) + reg_offset[index])) + +/* Specify the ABI registers (syscall argument passing, stack pointer). + * See sysdeps/unix/sysv/linux/${ARCH}/syscall.S from the GNU C Library. */ +#if defined(ARCH_ARM_EABI) + + static off_t reg_offset[] = { + [SYSARG_NUM] = USER_REGS_OFFSET(uregs[7]), + [SYSARG_1] = USER_REGS_OFFSET(uregs[0]), + [SYSARG_2] = USER_REGS_OFFSET(uregs[1]), + [SYSARG_3] = USER_REGS_OFFSET(uregs[2]), + [SYSARG_4] = USER_REGS_OFFSET(uregs[3]), + [SYSARG_5] = USER_REGS_OFFSET(uregs[4]), + [SYSARG_6] = USER_REGS_OFFSET(uregs[5]), + [SYSARG_RESULT] = USER_REGS_OFFSET(uregs[0]), + [STACK_POINTER] = USER_REGS_OFFSET(uregs[13]), + [INSTR_POINTER] = USER_REGS_OFFSET(uregs[15]), + [USERARG_1] = USER_REGS_OFFSET(uregs[0]), + }; + +#elif defined(ARCH_ARM64) + + #undef USER_REGS_OFFSET + #define USER_REGS_OFFSET(reg_name) offsetof(struct user_regs_struct, reg_name) + #define USER_REGS_OFFSET_32(reg_number) ((reg_number) * 4) + + static off_t reg_offset[] = { + [SYSARG_NUM] = USER_REGS_OFFSET(regs[8]), + [SYSARG_1] = USER_REGS_OFFSET(regs[0]), + [SYSARG_2] = USER_REGS_OFFSET(regs[1]), + [SYSARG_3] = USER_REGS_OFFSET(regs[2]), + [SYSARG_4] = USER_REGS_OFFSET(regs[3]), + [SYSARG_5] = USER_REGS_OFFSET(regs[4]), + [SYSARG_6] = USER_REGS_OFFSET(regs[5]), + [SYSARG_RESULT] = USER_REGS_OFFSET(regs[0]), + [STACK_POINTER] = USER_REGS_OFFSET(sp), + [INSTR_POINTER] = USER_REGS_OFFSET(pc), + [USERARG_1] = USER_REGS_OFFSET(regs[0]), + }; + + static off_t reg_offset_armeabi[] = { + [SYSARG_NUM] = USER_REGS_OFFSET_32(7), + [SYSARG_1] = USER_REGS_OFFSET_32(0), + [SYSARG_2] = USER_REGS_OFFSET_32(1), + [SYSARG_3] = USER_REGS_OFFSET_32(2), + [SYSARG_4] = USER_REGS_OFFSET_32(3), + [SYSARG_5] = USER_REGS_OFFSET_32(4), + [SYSARG_6] = USER_REGS_OFFSET_32(5), + [SYSARG_RESULT] = USER_REGS_OFFSET_32(0), + [STACK_POINTER] = USER_REGS_OFFSET_32(13), + [INSTR_POINTER] = USER_REGS_OFFSET_32(15), + [USERARG_1] = USER_REGS_OFFSET_32(0), + }; + + #undef REG + #define REG(tracee, version, index) \ + (*(word_t*) (tracee->is_aarch32 \ + ? (((uint8_t *) &tracee->_regs[version]) + reg_offset_armeabi[index]) \ + : (((uint8_t *) &tracee->_regs[version]) + reg_offset[index]))) + +#endif + +/** + * Return the *cached* value of the given @tracees' @reg. + */ +word_t peek_reg(const Tracee *tracee, RegVersion version, Reg reg) +{ + word_t result; + + assert(version < NB_REG_VERSION); + + result = REG(tracee, version, reg); + + /* Use only the 32 least significant bits (LSB) when running + * 32-bit processes on a 64-bit kernel. */ + if (is_32on64_mode(tracee)) + result &= 0xFFFFFFFF; + + return result; +} + +/** + * Set the *cached* value of the given @tracees' @reg. + */ +void poke_reg(Tracee *tracee, Reg reg, word_t value) +{ + if (peek_reg(tracee, CURRENT, reg) == value) + return; + +#ifdef ARCH_ARM64 + if (is_32on64_mode(tracee)) { + *(uint32_t *) ®(tracee, CURRENT, reg) = value; + } else +#endif + REG(tracee, CURRENT, reg) = value; + tracee->_regs_were_changed = true; +} + +/** + * Print the value of the current @tracee's registers according + * to the @verbose_level. Note: @message is mixed to the output. + */ +void print_current_regs(Tracee *tracee, int verbose_level, const char *message) +{ + if (tracee->verbose < verbose_level) + return; + + note(tracee, INFO, INTERNAL, + "pid %d: %s: %s(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx) = 0x%lx [0x%lx, %d]", + tracee->pid, message, + stringify_sysnum(get_sysnum(tracee, CURRENT)), + peek_reg(tracee, CURRENT, SYSARG_1), peek_reg(tracee, CURRENT, SYSARG_2), + peek_reg(tracee, CURRENT, SYSARG_3), peek_reg(tracee, CURRENT, SYSARG_4), + peek_reg(tracee, CURRENT, SYSARG_5), peek_reg(tracee, CURRENT, SYSARG_6), + peek_reg(tracee, CURRENT, SYSARG_RESULT), + peek_reg(tracee, CURRENT, STACK_POINTER), + get_abi(tracee)); +} + +/** + * Save the @tracee's current register bank into the @version register + * bank. + */ +void save_current_regs(Tracee *tracee, RegVersion version) +{ + /* Optimization: don't restore original register values if + * they were never changed. */ + if (version == ORIGINAL) + tracee->_regs_were_changed = false; + + memcpy(&tracee->_regs[version], &tracee->_regs[CURRENT], sizeof(tracee->_regs[CURRENT])); +} + +/** + * Copy all @tracee's general purpose registers into a dedicated + * cache. This function returns -errno if an error occured, 0 + * otherwise. + */ +int fetch_regs(Tracee *tracee) +{ + int status; + +#if defined(ARCH_ARM64) + struct iovec regs; + + regs.iov_base = &tracee->_regs[CURRENT]; + regs.iov_len = sizeof(tracee->_regs[CURRENT]); + + status = ptrace(PTRACE_GETREGSET, tracee->pid, NT_PRSTATUS, ®s); +#else + status = ptrace(PTRACE_GETREGS, tracee->pid, NULL, &tracee->_regs[CURRENT]); +#endif + if (status < 0) + return status; + + return 0; +} + +int push_specific_regs(Tracee *tracee, bool including_sysnum) +{ + int status; + + if (tracee->_regs_were_changed + || (tracee->restore_original_regs && tracee->restore_original_regs_after_seccomp_event)) { + /* At the very end of a syscall, with regard to the + * entry, only the result register can be modified by + * PRoot. */ + if (tracee->restore_original_regs) { + RegVersion restore_from = ORIGINAL; + if (tracee->restore_original_regs_after_seccomp_event) { + restore_from = ORIGINAL_SECCOMP_REWRITE; + tracee->restore_original_regs_after_seccomp_event = false; + } + +#define RESTORE(sysarg) (void) (reg_offset[SYSARG_RESULT] != reg_offset[sysarg] && \ + (REG(tracee, CURRENT, sysarg) = REG(tracee, restore_from, sysarg))) + + RESTORE(SYSARG_NUM); + RESTORE(SYSARG_1); + RESTORE(SYSARG_2); + RESTORE(SYSARG_3); + RESTORE(SYSARG_4); + RESTORE(SYSARG_5); + RESTORE(SYSARG_6); + RESTORE(STACK_POINTER); + } + +#if defined(ARCH_ARM64) + struct iovec regs; + word_t current_sysnum = REG(tracee, CURRENT, SYSARG_NUM); + + /* Update syscall number if needed. On arm64, a new + * subcommand has been added to PTRACE_{S,G}ETREGSET + * to allow write/read of current sycall number. */ + if (including_sysnum && current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { + regs.iov_base = ¤t_sysnum; + regs.iov_len = sizeof(current_sysnum); + status = ptrace(PTRACE_SETREGSET, tracee->pid, NT_ARM_SYSTEM_CALL, ®s); + if (status < 0) { + //note(tracee, WARNING, SYSTEM, "can't set the syscall number"); + return status; + } + } + + /* Update other registers. */ + regs.iov_base = &tracee->_regs[CURRENT]; + regs.iov_len = sizeof(tracee->_regs[CURRENT]); + + status = ptrace(PTRACE_SETREGSET, tracee->pid, NT_PRSTATUS, ®s); +#else + /* On ARM, a special ptrace request is required to + * change effectively the syscall number during a + * ptrace-stop. */ + word_t current_sysnum = REG(tracee, CURRENT, SYSARG_NUM); + if (including_sysnum && current_sysnum != REG(tracee, ORIGINAL, SYSARG_NUM)) { + status = ptrace(PTRACE_SET_SYSCALL, tracee->pid, 0, current_sysnum); + if (status < 0) { + //note(tracee, WARNING, SYSTEM, "can't set the syscall number"); + return status; + } + } + + status = ptrace(PTRACE_SETREGS, tracee->pid, NULL, &tracee->_regs[CURRENT]); +#endif + if (status < 0) + return status; + } + + return 0; +} + +/** + * Copy the cached values of all @tracee's general purpose registers + * back to the process, if necessary. This function returns -errno if + * an error occured, 0 otherwise. + */ +int push_regs(Tracee *tracee) { + return push_specific_regs(tracee, true); +} + +word_t get_systrap_size(Tracee *tracee) { +#if defined(ARCH_ARM_EABI) + /* On ARM thumb mode systrap size is 2 */ + if (tracee->_regs[CURRENT].ARM_cpsr & PSR_T_BIT) { + return 2; + } +#elif defined(ARCH_ARM64) + /* Same for AArch32, but we don't have nice macros */ + if (tracee->is_aarch32 && (((unsigned char *) &tracee->_regs[CURRENT])[0x40] & 0x20) != 0) { + return 2; + } +#else + (void) tracee; +#endif + return SYSTRAP_SIZE; +} diff --git a/app/src/main/cpp/proot/src/tracee/reg.h b/app/src/main/cpp/proot/src/tracee/reg.h new file mode 100644 index 0000000..4536388 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/reg.h @@ -0,0 +1,57 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef TRACEE_REG_H +#define TRACEE_REG_H + +#include "tracee/tracee.h" +#include "arch.h" + +typedef enum { + SYSARG_NUM = 0, + SYSARG_1, + SYSARG_2, + SYSARG_3, + SYSARG_4, + SYSARG_5, + SYSARG_6, + SYSARG_RESULT, + STACK_POINTER, + INSTR_POINTER, + RTLD_FINI, + STATE_FLAGS, + USERARG_1, +} Reg; + +extern int fetch_regs(Tracee *tracee); +extern int push_specific_regs(Tracee *tracee, bool including_sysnum); +extern int push_regs(Tracee *tracee); + +extern word_t peek_reg(const Tracee *tracee, RegVersion version, Reg reg); +extern void poke_reg(Tracee *tracee, Reg reg, word_t value); + +extern void print_current_regs(Tracee *tracee, int verbose_level, const char *message); +extern void save_current_regs(Tracee *tracee, RegVersion version); + +extern word_t get_systrap_size(Tracee *tracee); + +#endif /* TRACEE_REG_H */ diff --git a/app/src/main/cpp/proot/src/tracee/seccomp.c b/app/src/main/cpp/proot/src/tracee/seccomp.c new file mode 100644 index 0000000..2514aa2 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/seccomp.c @@ -0,0 +1,489 @@ +#include /* E*, */ +#include /* SIGSYS, */ +#include /* getpgid, */ +#include /* utimbuf, */ +#include /* statfs64 */ +#include /* memset */ +#include /* SYS_SENDMMSG */ +#include /* assert(3), */ +#include /* time(2), */ + +#include "cli/note.h" +#include "syscall/chain.h" +#include "syscall/syscall.h" +#include "tracee/seccomp.h" +#include "tracee/mem.h" +#include "path/path.h" + +static int handle_seccomp_event_common(Tracee *tracee); + +/** + * Restart syscall that caused seccomp event + * after changing it in tracee registers + * + * Syscall that will be restarted will be translated by proot + * so SIGSYS handler sees untranslated paths and should leave + * them untranslated. + */ +void restart_syscall_after_seccomp(Tracee* tracee) { + word_t instr_pointer; + + /* Enable restore regs at end of replaced call. */ + tracee->restore_original_regs_after_seccomp_event = true; + tracee->restart_how = PTRACE_SYSCALL; + + /* Move the instruction pointer back to the original trap */ + instr_pointer = peek_reg(tracee, CURRENT, INSTR_POINTER); + poke_reg(tracee, INSTR_POINTER, instr_pointer - get_systrap_size(tracee)); + + /* Write registers. (Omiting special sysnum logic as we're not during syscall + * execution, but we're queueing new syscall to be called) */ + push_specific_regs(tracee, false); +} + +/** + * Set specified result (negative for errno) and do not restart syscall. + */ +void set_result_after_seccomp(Tracee *tracee, word_t result) { + VERBOSE(tracee, 3, "Setting result after SIGSYS to 0x%lx", result); + poke_reg(tracee, SYSARG_RESULT, result); + push_specific_regs(tracee, false); +} + +/** + * Handle SIGSYS signal that was caused by system seccomp policy. + * + * Return 0 to swallow signal or SIGSYS to deliver it to process. + */ +int handle_seccomp_event(Tracee* tracee) +{ + int ret; + + /* Reset status so next SIGTRAP | 0x80 is + * recognized as syscall entry. */ + tracee->status = 0; + + /* Registers are never restored at this stage as they weren't saved. */ + tracee->restore_original_regs = false; + + /* Fetch registers. */ + ret = fetch_regs(tracee); + if (ret != 0) { + VERBOSE(tracee, 1, "Couldn't fetch regs on seccomp SIGSYS"); + return SIGSYS; + } + + /* Save regs so they can be restored at end of replaced call. */ + save_current_regs(tracee, ORIGINAL_SECCOMP_REWRITE); + + print_current_regs(tracee, 3, "seccomp SIGSYS"); + + return handle_seccomp_event_common(tracee); +} + +void fix_and_restart_enosys_syscall(Tracee* tracee) +{ + /* Reset tracee state so we're not handling syscall exit */ + tracee->status = 0; + tracee->restore_original_regs = false; + + /* Restore and save original registers */ + memcpy(&tracee->_regs[CURRENT], &tracee->_regs[ORIGINAL], sizeof(tracee->_regs[CURRENT])); + save_current_regs(tracee, ORIGINAL_SECCOMP_REWRITE); + + handle_seccomp_event_common(tracee); +} + +static int handle_seccomp_event_common(Tracee *tracee) +{ + int ret; + Sysnum sysnum; + + sysnum = get_sysnum(tracee, CURRENT); + + switch (sysnum) { + case PR_open: + set_sysnum(tracee, PR_openat); + poke_reg(tracee, SYSARG_4, peek_reg(tracee, CURRENT, SYSARG_3)); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + + case PR_accept: + set_sysnum(tracee, PR_accept4); + poke_reg(tracee, SYSARG_4, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_setgroups: + case PR_setgroups32: + set_result_after_seccomp(tracee, 0); + break; + + case PR_getpgrp: + /* Query value with getpgid and set it as result. */ + set_result_after_seccomp(tracee, getpgid(tracee->pid)); + break; + + case PR_symlink: + set_sysnum(tracee, PR_symlinkat); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + + case PR_link: + set_sysnum(tracee, PR_linkat); + poke_reg(tracee, SYSARG_4, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + poke_reg(tracee, SYSARG_3, AT_FDCWD); + poke_reg(tracee, SYSARG_5, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_chmod: + set_sysnum(tracee, PR_fchmodat); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + poke_reg(tracee, SYSARG_4, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_chown: + case PR_lchown: + case PR_chown32: + case PR_lchown32: + set_sysnum(tracee, PR_fchownat); + poke_reg(tracee, SYSARG_4, peek_reg(tracee, CURRENT, SYSARG_3)); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + if (sysnum == PR_lchown || sysnum == PR_lchown32) { + poke_reg(tracee, SYSARG_5, AT_SYMLINK_NOFOLLOW); + } else { + poke_reg(tracee, SYSARG_5, 0); + } + restart_syscall_after_seccomp(tracee); + break; + + case PR_unlink: + case PR_rmdir: + set_sysnum(tracee, PR_unlinkat); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + poke_reg(tracee, SYSARG_3, sysnum==PR_rmdir ? AT_REMOVEDIR : 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_send: + set_sysnum(tracee, PR_sendto); + poke_reg(tracee, SYSARG_5, 0); + poke_reg(tracee, SYSARG_6, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_recv: + set_sysnum(tracee, PR_recvfrom); + poke_reg(tracee, SYSARG_5, 0); + poke_reg(tracee, SYSARG_6, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_waitpid: + set_sysnum(tracee, PR_wait4); + poke_reg(tracee, SYSARG_4, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_statfs: + { + int size; + char path[PATH_MAX]; + char original[PATH_MAX]; + char devshm_path[PATH_MAX]; + struct statfs64 mstatfs64; + struct compat_statfs mstatfs; + size = read_string(tracee, original, peek_reg(tracee, CURRENT, SYSARG_1), PATH_MAX); + if (size < 0) { + set_result_after_seccomp(tracee, size); + break; + } + if (size >= PATH_MAX) { + set_result_after_seccomp(tracee, -ENAMETOOLONG); + break; + } + translate_path(tracee, path, AT_FDCWD, original, true); + errno = 0; + statfs64(path, &mstatfs64); + if (errno != 0) { + set_result_after_seccomp(tracee, -errno); + break; + } + + /* Fake /dev/shm being tmpfs, see statfs handler in syscall/exit.c */ + if (translate_path(tracee, devshm_path, AT_FDCWD, "/dev/shm", true) >= 0) { + Comparison comparison = compare_paths(devshm_path, path); + if (comparison == PATHS_ARE_EQUAL || comparison == PATH1_IS_PREFIX) { + mstatfs64.f_type = 0x01021994; + } + } + + if ((mstatfs64.f_blocks | mstatfs64.f_bfree | mstatfs64.f_bavail | + mstatfs64.f_bsize | mstatfs64.f_frsize | mstatfs64.f_files | + mstatfs64.f_ffree) & 0xffffffff00000000ULL) { + set_result_after_seccomp(tracee, -EOVERFLOW); + break; + } + + mstatfs.f_type = mstatfs64.f_type; + mstatfs.f_bsize = mstatfs64.f_bsize; + mstatfs.f_blocks = mstatfs64.f_blocks; + mstatfs.f_bfree = mstatfs64.f_bfree; + mstatfs.f_bavail = mstatfs64.f_bavail; + mstatfs.f_files = mstatfs64.f_files; + mstatfs.f_ffree = mstatfs64.f_ffree; + mstatfs.f_fsid = mstatfs64.f_fsid; + mstatfs.f_namelen = mstatfs64.f_namelen; + mstatfs.f_frsize = mstatfs64.f_frsize; + mstatfs.f_flags = mstatfs64.f_flags; + memset(mstatfs.f_spare, 0, sizeof(mstatfs.f_spare)); + + write_data(tracee, peek_reg(tracee, CURRENT, SYSARG_2), &mstatfs, sizeof(struct compat_statfs)); + set_result_after_seccomp(tracee, 0); + break; + } + + case PR_utimes: + { + /* int utimes(const char *filename, const struct timeval times[2]); + * + * convert to: + * int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags); */ + struct timeval times[2]; + struct timespec timens[2]; + + set_sysnum(tracee, PR_utimensat); + if (peek_reg(tracee, CURRENT, SYSARG_2) != 0) { + ret = read_data(tracee, times, peek_reg(tracee, CURRENT, SYSARG_2), sizeof(times)); + if (ret < 0) { + set_result_after_seccomp(tracee, ret); + break; + } + timens[0].tv_sec = (time_t)times[0].tv_sec; + timens[0].tv_nsec = (long)times[0].tv_usec * 1000; + timens[1].tv_sec = (time_t)times[1].tv_sec; + timens[1].tv_nsec = (long)times[1].tv_usec * 1000; + ret = set_sysarg_data(tracee, timens, sizeof(timens), SYSARG_2); + if (ret < 0) { + set_result_after_seccomp(tracee, ret); + break; + } + } + poke_reg(tracee, SYSARG_4, 0); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + } + + case PR_utime: + { + /* int utime(const char *filename, const struct utimbuf *times); + * + * convert to: + * int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags); */ + struct utimbuf times; + struct timespec timens[2]; + + set_sysnum(tracee, PR_utimensat); + if (peek_reg(tracee, CURRENT, SYSARG_2) != 0) { + ret = read_data(tracee, ×, peek_reg(tracee, CURRENT, SYSARG_2), sizeof(times)); + if (ret < 0) { + set_result_after_seccomp(tracee, ret); + break; + } + timens[0].tv_sec = (time_t)times.actime; + timens[0].tv_nsec = 0; + timens[1].tv_sec = (time_t)times.modtime; + timens[1].tv_nsec = 0; + ret = set_sysarg_data(tracee, timens, sizeof(timens), SYSARG_2); + if (ret < 0) { + set_result_after_seccomp(tracee, ret); + break; + } + } + poke_reg(tracee, SYSARG_4, 0); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + } + + case PR_stat: + case PR_lstat: + set_sysnum(tracee, PR_newfstatat); + poke_reg(tracee, SYSARG_4, sysnum == PR_lstat ? AT_SYMLINK_NOFOLLOW : 0); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + + case PR_pipe: + set_sysnum(tracee, PR_pipe2); + poke_reg(tracee, SYSARG_2, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_dup2: + set_sysnum(tracee, PR_dup3); + poke_reg(tracee, SYSARG_3, 0); + restart_syscall_after_seccomp(tracee); + break; + + case PR_access: + set_sysnum(tracee, PR_faccessat); + poke_reg(tracee, SYSARG_4, 0); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + + case PR_mkdir: + set_sysnum(tracee, PR_mkdirat); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + + case PR_rename: + set_sysnum(tracee, PR_renameat); + poke_reg(tracee, SYSARG_4, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_3, AT_FDCWD); + poke_reg(tracee, SYSARG_2, peek_reg(tracee, CURRENT, SYSARG_1)); + poke_reg(tracee, SYSARG_1, AT_FDCWD); + restart_syscall_after_seccomp(tracee); + break; + + case PR_select: + { + word_t timeval_arg = peek_reg(tracee, CURRENT, SYSARG_5); + word_t timespec_arg = 0; + if (timeval_arg != 0) { + struct timeval tv = {}; + if (read_data(tracee, &tv, timeval_arg, sizeof(tv))) { + set_result_after_seccomp(tracee, -EFAULT); + break; + } + if (tv.tv_usec >= 1000000 || tv.tv_usec < 0) { + set_result_after_seccomp(tracee, -EINVAL); + break; + } + struct timespec ts = { + .tv_sec = tv.tv_sec, + .tv_nsec = tv.tv_usec * 1000 + }; + timespec_arg = alloc_mem(tracee, sizeof(ts)); + if(write_data(tracee, timespec_arg, &ts, sizeof(ts))) { + set_result_after_seccomp(tracee, -EFAULT); + break; + } + } + set_sysnum(tracee, PR_pselect6); + poke_reg(tracee, SYSARG_5, timespec_arg); + poke_reg(tracee, SYSARG_6, 0); + restart_syscall_after_seccomp(tracee); + break; + } + + case PR_poll: + { + int ms_arg = (int) peek_reg(tracee, CURRENT, SYSARG_3); + word_t timespec_arg = 0; + if (ms_arg >= 0) { + struct timespec ts = { + .tv_sec = ms_arg / 1000, + .tv_nsec = (ms_arg % 1000) * 1000000 + }; + timespec_arg = alloc_mem(tracee, sizeof(ts)); + if(write_data(tracee, timespec_arg, &ts, sizeof(ts))) { + set_result_after_seccomp(tracee, -EFAULT); + break; + } + } + set_sysnum(tracee, PR_ppoll); + poke_reg(tracee, SYSARG_3, timespec_arg); + poke_reg(tracee, SYSARG_4, 0); + poke_reg(tracee, SYSARG_5, 0); + restart_syscall_after_seccomp(tracee); + break; + } + + case PR_time: + { + time_t t = time(NULL); + word_t addr = peek_reg(tracee, CURRENT, SYSARG_1); + errno = 0; + if (addr != 0) { + poke_word(tracee, addr, t); + } + set_result_after_seccomp(tracee, errno ? -EFAULT : t); + break; + } + + case PR_ftruncate: + { + if (detranslate_sysnum(get_abi(tracee), PR_ftruncate64) == SYSCALL_AVOIDER) { + set_result_after_seccomp(tracee, -ENOSYS); + break; + } + set_sysnum(tracee, PR_ftruncate64); + poke_reg(tracee, SYSARG_3, peek_reg(tracee, CURRENT, SYSARG_2)); + poke_reg(tracee, SYSARG_2, 0); + poke_reg(tracee, SYSARG_4, 0); + restart_syscall_after_seccomp(tracee); + break; + } + + case PR_setresuid: + case PR_setresgid: + { + gid_t rxid, exid, sxid, rxid_, exid_, sxid_; + rxid = peek_reg(tracee, CURRENT, SYSARG_1); + exid = peek_reg(tracee, CURRENT, SYSARG_2); + sxid = peek_reg(tracee, CURRENT, SYSARG_3); + if (sysnum == PR_setresuid) + ret = getresuid(&rxid_, &exid_, &sxid_); + else if (sysnum == PR_setresgid) + ret = getresgid(&rxid_, &exid_, &sxid_); + if (ret) { // EFAULT = address outside address space + set_result_after_seccomp(tracee, -EPERM); + break; + } + ret = 0; + if (rxid != rxid_ && rxid != -1) + ret = -EPERM; + if (exid != exid_ && exid != -1) + ret = -EPERM; + if (sxid != sxid_ && sxid != -1) + ret = -EPERM; + set_result_after_seccomp(tracee, ret); + break; + } + + case PR_set_robust_list: + default: + /* Set errno to -ENOSYS */ + set_result_after_seccomp(tracee, -ENOSYS); + } + + return 0; +} diff --git a/app/src/main/cpp/proot/src/tracee/seccomp.h b/app/src/main/cpp/proot/src/tracee/seccomp.h new file mode 100644 index 0000000..61d9c1c --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/seccomp.h @@ -0,0 +1,22 @@ +#include "tracee/tracee.h" +#include "sys/vfs.h" + +struct compat_statfs { + int f_type; + int f_bsize; + int f_blocks; + int f_bfree; + int f_bavail; + int f_files; + int f_ffree; + fsid_t f_fsid; + int f_namelen; + int f_frsize; + int f_flags; + int f_spare[4]; +}; + +void restart_syscall_after_seccomp(Tracee* tracee); +void set_result_after_seccomp(Tracee *tracee, word_t result); +int handle_seccomp_event(Tracee* tracee); +void fix_and_restart_enosys_syscall(Tracee* tracee); diff --git a/app/src/main/cpp/proot/src/tracee/tracee.c b/app/src/main/cpp/proot/src/tracee/tracee.c new file mode 100644 index 0000000..16a9f19 --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/tracee.c @@ -0,0 +1,586 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#include /* CLONE_*, */ +#include /* pid_t, size_t, */ +#include /* NULL, */ +#include /* assert(3), */ +#include /* bzero(3), */ +#include /* bool, true, false, */ +#include /* LIST_*, */ +#include /* talloc_*, */ +#include /* kill(2), SIGKILL, */ +#include /* ptrace(2), PTRACE_*, */ +#include /* E*, */ +#include /* PRI*, */ +#include + +#include "tracee/tracee.h" +#include "tracee/reg.h" +#include "path/binding.h" +#include "syscall/sysnum.h" +#include "tracee/event.h" +#include "ptrace/ptrace.h" +#include "ptrace/wait.h" +#include "cli/note.h" + +#include "compat.h" + +static Tracees tracees; + + +/** + * Remove @zombie from its parent's list of zombies. Note: this is a + * talloc destructor. + */ +static int remove_zombie(Tracee *zombie) +{ + LIST_REMOVE(zombie, link); + return 0; +} + +/** + * Perform some specific treatments against @pointer according to its + * type, before it gets unlinked from @tracee_->life_context. + */ +static void clean_life_span_object(const void *pointer, int depth UNUSED, + int max_depth UNUSED, int is_ref UNUSED, void *tracee_) +{ + Binding *binding; + Tracee *tracee; + + tracee = talloc_get_type_abort(tracee_, Tracee); + + /* So far, only bindings need a special treatment. */ + binding = talloc_get_type(pointer, Binding); + if (binding != NULL) + remove_binding_from_all_lists(tracee, binding); +} + +/** + * Remove @tracee from the list of tracees and update all of its + * children & ptracees, and its ptracer. Note: this is a talloc + * destructor. + */ +static int remove_tracee(Tracee *tracee) +{ + Tracee *relative; + Tracee *ptracer; + int event; + + LIST_REMOVE(tracee, link); + + /* Clean objects that are linked to this tracee's life + * span. */ + talloc_report_depth_cb(tracee->life_context, 0, 100, clean_life_span_object, tracee); + + /* This could be optimize by using a dedicated list of + * children and ptracees. */ + LIST_FOREACH(relative, &tracees, link) { + /* Its children are now orphan. */ + if (relative->parent == tracee) + relative->parent = NULL; + + /* Its tracees are now free. */ + if (relative->as_ptracee.ptracer == tracee) { + /* Release the pending event, if any. */ + relative->as_ptracee.ptracer = NULL; + + if (relative->as_ptracee.event4.proot.pending) { + event = handle_tracee_event(relative, + relative->as_ptracee.event4.proot.value); + (void) restart_tracee(relative, event); + } + else if (relative->as_ptracee.event4.ptracer.pending) { + event = relative->as_ptracee.event4.proot.value; + (void) restart_tracee(relative, event); + } + + bzero(&relative->as_ptracee, sizeof(relative->as_ptracee)); + } + } + + /* Nothing else to do if it's not a ptracee. */ + ptracer = tracee->as_ptracee.ptracer; + if (ptracer == NULL) + return 0; + + /* Zombify this ptracee until its ptracer is notified about + * its death. */ + event = tracee->as_ptracee.event4.ptracer.value; + if (tracee->as_ptracee.event4.ptracer.pending + && (WIFEXITED(event) || WIFSIGNALED(event))) { + Tracee *zombie; + + zombie = new_dummy_tracee(ptracer); + if (zombie != NULL) { + LIST_INSERT_HEAD(&PTRACER.zombies, zombie, link); + talloc_set_destructor(zombie, remove_zombie); + + zombie->parent = tracee->parent; + zombie->clone = tracee->clone; + zombie->pid = tracee->pid; + + detach_from_ptracer(tracee); + attach_to_ptracer(zombie, ptracer); + + zombie->as_ptracee.event4.ptracer.pending = true; + zombie->as_ptracee.event4.ptracer.value = event; + zombie->as_ptracee.is_zombie = true; + + return 0; + } + /* Fallback to the common path. */ + } + + detach_from_ptracer(tracee); + + /* Wake its ptracer if there's nothing else to wait for. */ + if (PTRACER.nb_ptracees == 0 && PTRACER.wait_pid != 0) { + /* Update the return value of ptracer's wait(2). */ + poke_reg(ptracer, SYSARG_RESULT, -ECHILD); + + /* Don't forget to write its register cache back. */ + (void) push_regs(ptracer); + + PTRACER.wait_pid = 0; + (void) restart_tracee(ptracer, 0); + } + + return 0; +} + +/** + * Allocate a new entry for a dummy tracee (no pid, no destructor, not + * in the list of tracees, ...). The new allocated memory is attached + * to the given @context. This function returns NULL if an error + * occurred (ENOMEM), otherwise it returns the newly allocated + * structure. + */ +Tracee *new_dummy_tracee(TALLOC_CTX *context) +{ + Tracee *tracee; + + tracee = talloc_zero(context, Tracee); + if (tracee == NULL) + return NULL; + + /* Allocate a memory collector. */ + tracee->ctx = talloc_new(tracee); + if (tracee->ctx == NULL) + goto no_mem; + + /* By default new tracees have an empty file-system + * name-space and heap. */ + tracee->fs = talloc_zero(tracee, FileSystemNameSpace); + tracee->heap = talloc_zero(tracee, Heap); + if (tracee->fs == NULL || tracee->heap == NULL) + goto no_mem; + + return tracee; + +no_mem: + TALLOC_FREE(tracee); + return NULL; +} + +/** + * Allocate a new entry for the tracee @pid, then set its destructor + * and add it to the list of tracees. This function returns NULL if + * an error occurred (ENOMEM), otherwise it returns the newly + * allocated structure. + */ +static Tracee *new_tracee(pid_t pid) +{ + Tracee *tracee; + + tracee = new_dummy_tracee(NULL); + if (tracee == NULL) + return NULL; + + talloc_set_destructor(tracee, remove_tracee); + + tracee->pid = pid; + + LIST_INSERT_HEAD(&tracees, tracee, link); + + tracee->life_context = talloc_new(tracee); + + return tracee; +} + +/** + * Return the first [stopped?] tracee with the given + * @pid (-1 for any) which has the given @ptracer, and which has a + * pending event for its ptracer if @only_with_pevent is true. See + * wait(2) manual for the meaning of @wait_options. This function + * returns NULL if there's no such ptracee. + */ +static Tracee *get_ptracee(const Tracee *ptracer, pid_t pid, bool only_stopped, + bool only_with_pevent, word_t wait_options) +{ + Tracee *ptracee; + + /* Return zombies first. */ + LIST_FOREACH(ptracee, &PTRACER.zombies, link) { + /* Not the ptracee you're looking for? */ + if (pid != ptracee->pid && pid != -1) + continue; + + /* Not the expected kind of cloned process? */ + if (!EXPECTED_WAIT_CLONE(wait_options, ptracee)) + continue; + + return ptracee; + } + + LIST_FOREACH(ptracee, &tracees, link) { + /* Discard tracees that don't have this ptracer. */ + if (PTRACEE.ptracer != ptracer) + continue; + + /* Not the ptracee you're looking for? */ + if (pid != ptracee->pid && pid != -1) + continue; + + /* Not the expected kind of cloned process? */ + if (!EXPECTED_WAIT_CLONE(wait_options, ptracee)) + continue; + + /* No need to do more checks if its stopped state + * doesn't matter. Be careful when using such + * maybe-running tracee. */ + if (!only_stopped) + return ptracee; + + /* Is this tracee in the stopped state? */ + if (ptracee->running) + continue; + + /* Has a pending event for its ptracer? */ + if (PTRACEE.event4.ptracer.pending || !only_with_pevent) + return ptracee; + + /* No need to go further if the specific tracee isn't + * in the expected state? */ + if (pid == ptracee->pid) + return NULL; + } + + return NULL; +} + +/** + * Wrapper for get_ptracee(), this ensures only a stopped tracee is + * returned (or NULL). + */ +Tracee *get_stopped_ptracee(const Tracee *ptracer, pid_t pid, + bool only_with_pevent, word_t wait_options) +{ + return get_ptracee(ptracer, pid, true, only_with_pevent, wait_options); +} + +/** + * Wrapper for get_ptracee(), this ensures no running tracee is + * returned. + */ +bool has_ptracees(const Tracee *ptracer, pid_t pid, word_t wait_options) +{ + return (get_ptracee(ptracer, pid, false, false, wait_options) != NULL); +} + +/** + * Return the entry related to the tracee @pid. If no entry were + * found, a new one is created if @create is true, otherwise NULL is + * returned. + */ +Tracee *get_tracee(const Tracee *current_tracee, pid_t pid, bool create) +{ + Tracee *tracee; + + /* Don't reset the memory collector if the searched tracee is + * the current one: there's likely pointers to the + * sub-allocated data in the caller. */ + if (current_tracee != NULL && current_tracee->pid == pid) + return (Tracee *)current_tracee; + + LIST_FOREACH(tracee, &tracees, link) { + if (tracee->pid == pid) { + /* Flush then allocate a new memory collector. */ + TALLOC_FREE(tracee->ctx); + tracee->ctx = talloc_new(tracee); + + return tracee; + } + } + + return (create ? new_tracee(pid) : NULL); +} + +/** + * Mark tracee as terminated and optionally take action. + */ +void terminate_tracee(Tracee *tracee) +{ + tracee->terminated = true; + + /* Case where the terminated tracee is marked + to kill all tracees on exit. + */ + if (tracee->killall_on_exit) { + VERBOSE(tracee, 1, "terminating all tracees on exit"); + kill_all_tracees(); + } +} + +/** + * Free all tracees marked as terminated. + */ +void free_terminated_tracees() +{ + Tracee *next; + + /* Items can't be deleted when using LIST_FOREACH. */ + next = tracees.lh_first; + while (next != NULL) { + Tracee *tracee = next; + next = tracee->link.le_next; + + if (tracee->terminated) + TALLOC_FREE(tracee); + } +} + +/** + * Make new @parent's child inherit from it. Depending on + * @clone_flags, some information are copied or shared. This function + * returns -errno if an error occured, otherwise 0. + */ +int new_child(Tracee *parent, word_t clone_flags) +{ + int ptrace_options; + unsigned long pid; + Tracee *child; + int status; + + /* If the tracee calls clone(2) with the CLONE_VFORK flag, + * PTRACE_EVENT_VFORK will be delivered instead [...]; + * otherwise if the tracee calls clone(2) with the exit signal + * set to SIGCHLD, PTRACE_EVENT_FORK will be delivered [...] + * + * -- ptrace(2) man-page + * + * That means we have to check if it's actually a clone(2) in + * order to get the right flags. + */ + status = fetch_regs(parent); + if (status >= 0 && get_sysnum(parent, CURRENT) == PR_clone) + clone_flags = peek_reg(parent, CURRENT, SYSARG_1); + + /* Get the pid of the parent's new child. */ + status = ptrace(PTRACE_GETEVENTMSG, parent->pid, NULL, &pid); + if (status < 0 || pid == 0) { + note(parent, WARNING, SYSTEM, "ptrace(GETEVENTMSG)"); + return status; + } + + child = get_tracee(parent, (pid_t) pid, true); + if (child == NULL) { + note(parent, WARNING, SYSTEM, "running out of memory"); + return -ENOMEM; + } + + /* Sanity checks. */ + assert(child != NULL + && child->exe == NULL + && child->fs->cwd == NULL + && child->fs->bindings.pending == NULL + && child->fs->bindings.guest == NULL + && child->fs->bindings.host == NULL + && child->glue == NULL + && child->parent == NULL + && child->as_ptracee.ptracer == NULL); + + child->verbose = parent->verbose; + child->seccomp = parent->seccomp; + child->sysexit_pending = parent->sysexit_pending; +#ifdef ARCH_ARM64 + child->is_aarch32 = parent->is_aarch32; +#endif + + /* If CLONE_VM is set, the calling process and the child + * process run in the same memory space [...] any memory + * mapping or unmapping performed with mmap(2) or munmap(2) by + * the child or calling process also affects the other + * process. + * + * If CLONE_VM is not set, the child process runs in a + * separate copy of the memory space of the calling process at + * the time of clone(). Memory writes or file + * mappings/unmappings performed by one of the processes do + * not affect the other, as with fork(2). + * + * -- clone(2) man-page + */ + TALLOC_FREE(child->heap); + child->heap = ((clone_flags & CLONE_VM) != 0) + ? talloc_reference(child, parent->heap) + : talloc_memdup(child, parent->heap, sizeof(Heap)); + if (child->heap == NULL) + return -ENOMEM; + + child->load_info = talloc_reference(child, parent->load_info); + + /* If CLONE_PARENT is set, then the parent of the new child + * (as returned by getppid(2)) will be the same as that of the + * calling process. + * + * If CLONE_PARENT is not set, then (as with fork(2)) the + * child's parent is the calling process. + * + * -- clone(2) man-page + */ + if ((clone_flags & CLONE_PARENT) != 0) + child->parent = parent->parent; + else + child->parent = parent; + + /* Remember if this child belongs to the same thread group as + * its parent. This is currently useful for ptrace emulation + * only but it deserves to be extended to support execve(2) + * specificity (ie. when a thread calls execve(2), its pid + * gets replaced by the pid of its thread group leader). */ + child->clone = ((clone_flags & CLONE_THREAD) != 0); + + /* Depending on how the new process is created, it may be + * automatically traced by the parent's tracer. */ + ptrace_options = ( clone_flags == 0 ? PTRACE_O_TRACEFORK + : (clone_flags & 0xFF) == SIGCHLD ? PTRACE_O_TRACEFORK + : (clone_flags & CLONE_VFORK) != 0 ? PTRACE_O_TRACEVFORK + : PTRACE_O_TRACECLONE); + if (parent->as_ptracee.ptracer != NULL + && ( (ptrace_options & parent->as_ptracee.options) != 0 + || (clone_flags & CLONE_PTRACE) != 0)) { + attach_to_ptracer(child, parent->as_ptracee.ptracer); + + /* All these flags are inheritable, no matter why this + * child is being traced. */ + child->as_ptracee.options |= (parent->as_ptracee.options + & ( PTRACE_O_TRACECLONE + | PTRACE_O_TRACEEXEC + | PTRACE_O_TRACEEXIT + | PTRACE_O_TRACEFORK + | PTRACE_O_TRACESYSGOOD + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEVFORKDONE)); + } + + /* If CLONE_FS is set, the parent and the child process share + * the same file system information. This includes the root + * of the file system, the current working directory, and the + * umask. Any call to chroot(2), chdir(2), or umask(2) + * performed by the parent process or the child process also + * affects the other process. + * + * If CLONE_FS is not set, the child process works on a copy + * of the file system information of the parent process at the + * time of the clone() call. Calls to chroot(2), chdir(2), + * umask(2) performed later by one of the processes do not + * affect the other process. + * + * -- clone(2) man-page + */ + TALLOC_FREE(child->fs); + if ((clone_flags & CLONE_FS) != 0) { + /* File-system name-space is shared. */ + child->fs = talloc_reference(child, parent->fs); + } + else { + /* File-system name-space is copied. */ + child->fs = talloc_zero(child, FileSystemNameSpace); + if (child->fs == NULL) + return -ENOMEM; + + child->fs->cwd = talloc_strdup(child->fs, parent->fs->cwd); + if (child->fs->cwd == NULL) + return -ENOMEM; + talloc_set_name_const(child->fs->cwd, "$cwd"); + + /* Bindings are shared across file-system name-spaces since a + * "mount --bind" made by a process affects all other processes + * under Linux. Actually they are copied when a sub + * reconfiguration occured (nested proot or chroot(2)). */ + child->fs->bindings.guest = talloc_reference(child->fs, parent->fs->bindings.guest); + child->fs->bindings.host = talloc_reference(child->fs, parent->fs->bindings.host); + } + + /* The path to the executable is unshared only once the child + * process does a call to execve(2). */ + child->exe = talloc_reference(child, parent->exe); + + child->glue = talloc_reference(child, parent->glue); + + /* Restart the child tracee if it was already alive but + * stopped until that moment. */ + if (child->sigstop == SIGSTOP_PENDING) { + bool keep_stopped = false; + + child->sigstop = SIGSTOP_ALLOWED; + + /* Notify its ptracer if it is ready to be traced. */ + if (child->as_ptracee.ptracer != NULL) { + /* Sanity check. */ + assert(!child->as_ptracee.tracing_started); + +#ifndef __W_STOPCODE + #define __W_STOPCODE(sig) ((sig) << 8 | 0x7f) +#endif + keep_stopped = handle_ptracee_event(child, __W_STOPCODE(SIGSTOP)); + + /* Note that this event was already handled by + * PRoot since child->as_ptracee.ptracer was + * NULL up to now. */ + child->as_ptracee.event4.proot.pending = false; + child->as_ptracee.event4.proot.value = 0; + } + + if (!keep_stopped) + (void) restart_tracee(child, 0); + } + + VERBOSE(child, 1, "new child: %d", child->pid); + + return 0; +} + +/* Send the KILL signal to all tracees. */ +void kill_all_tracees() +{ + Tracee *tracee; + + LIST_FOREACH(tracee, &tracees, link) + kill(tracee->pid, SIGKILL); +} + +Tracees *get_tracees_list_head() { + return &tracees; +} diff --git a/app/src/main/cpp/proot/src/tracee/tracee.h b/app/src/main/cpp/proot/src/tracee/tracee.h new file mode 100644 index 0000000..edfdf1e --- /dev/null +++ b/app/src/main/cpp/proot/src/tracee/tracee.h @@ -0,0 +1,275 @@ +/* -*- c-set-style: "K&R"; c-basic-offset: 8 -*- + * + * This file is part of PRoot. + * + * Copyright (C) 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA. + */ + +#ifndef TRACEE_H +#define TRACEE_H + +#include /* pid_t, size_t, */ +#include /* struct user*, */ +#include /* bool, */ +#include /* LIST_*, */ +#include /* enum __ptrace_request */ +#include /* talloc_*, */ +#include /* *int*_t, */ + +#include "arch.h" /* word_t, user_regs_struct */ +#include "compat.h" + +typedef enum { + CURRENT = 0, + ORIGINAL = 1, + MODIFIED = 2, + ORIGINAL_SECCOMP_REWRITE = 3, + NB_REG_VERSION +} RegVersion; + +struct bindings; +struct load_info; +struct chained_syscalls; + +/* Information related to a file-system name-space. */ +typedef struct { + struct { + /* List of bindings as specified by the user but not canonicalized yet. */ + struct bindings *pending; + + /* List of bindings canonicalized and sorted in the "guest" order. */ + struct bindings *guest; + + /* List of bindings canonicalized and sorted in the "host" order. */ + struct bindings *host; + } bindings; + + /* Current working directory, à la /proc/self/pwd. */ + char *cwd; +} FileSystemNameSpace; + +/* Virtual heap, emulated with a regular memory mapping. */ +typedef struct { + word_t base; + size_t size; + bool disabled; +} Heap; + +/* Information related to a tracee process. */ +typedef struct tracee { + /********************************************************************** + * Private resources * + **********************************************************************/ + + /* Link for the list of all tracees. */ + LIST_ENTRY(tracee) link; + + /* Process identifier. */ + pid_t pid; + + /* Is it currently running or not? */ + bool running; + + /* Is this tracee ready to be freed? + * dedicated to terminated tracees instead. */ + bool terminated; + + /* Whether termination of this tracee implies an immediate kill + * of all tracees. */ + bool killall_on_exit; + + /* Parent of this tracee, NULL if none. */ + struct tracee *parent; + + /* Is it a "clone", i.e has the same parent as its creator. */ + bool clone; + + /* Support for ptrace emulation (tracer side). */ + struct { + size_t nb_ptracees; + LIST_HEAD(zombies, tracee) zombies; + + pid_t wait_pid; + word_t wait_options; + + enum { + DOESNT_WAIT = 0, + WAITS_IN_KERNEL, + WAITS_IN_PROOT + } waits_in; + } as_ptracer; + + /* Support for ptrace emulation (tracee side). */ + struct { + struct tracee *ptracer; + + struct { + #define STRUCT_EVENT struct { int value; bool pending; } + + STRUCT_EVENT proot; + STRUCT_EVENT ptracer; + } event4; + + bool tracing_started; + bool ignore_loader_syscalls; + bool ignore_syscalls; + word_t options; + bool is_zombie; + } as_ptracee; + + /* Current status: + * 0: enter syscall + * 1: exit syscall no error + * -errno: exit syscall with error. */ + int status; + +#define IS_IN_SYSENTER(tracee) ((tracee)->status == 0) +#define IS_IN_SYSEXIT(tracee) (!IS_IN_SYSENTER(tracee)) +#define IS_IN_SYSEXIT2(tracee, sysnum) (IS_IN_SYSEXIT(tracee) \ + && get_sysnum((tracee), ORIGINAL) == sysnum) + + /* How this tracee is restarted. */ + int restart_how, last_restart_how; + + /* Value of the tracee's general purpose registers. */ + struct user_regs_struct _regs[NB_REG_VERSION]; + bool _regs_were_changed; + bool restore_original_regs; + bool restore_original_regs_after_seccomp_event; + + /* State for the special handling of SIGSTOP. */ + enum { + SIGSTOP_IGNORED = 0, /* Ignore SIGSTOP (once the parent is known). */ + SIGSTOP_ALLOWED, /* Allow SIGSTOP (once the parent is known). */ + SIGSTOP_PENDING, /* Block SIGSTOP until the parent is unknown. */ + } sigstop; + + /* True if next SIGSYS caused by seccomp should be silently dropped + * without affecting state of any registers. */ + bool skip_next_seccomp_signal; + + /* Context used to collect all the temporary dynamic memory + * allocations. */ + TALLOC_CTX *ctx; + + /* Context used to collect all dynamic memory allocations that + * should be released once this tracee is freed. */ + TALLOC_CTX *life_context; + + /* Note: I could rename "ctx" in "event_span" and + * "life_context" in "life_span". */ + + /* Specify the type of the final component during the + * initialization of a binding. This variable is first + * defined in bind_path() then used in build_glue(). */ + mode_t glue_type; + + /* During a sub-reconfiguration, the new setup is relatively + * to @tracee's file-system name-space. Also, @paths holds + * its $PATH environment variable in order to emulate the + * execvp(3) behavior. */ + struct { + struct tracee *tracee; + const char *paths; + } reconf; + + /* Unrequested syscalls inserted by PRoot after an actual + * syscall. */ + struct { + struct chained_syscalls *syscalls; + bool force_final_result; + word_t final_result; + enum { + SYSNUM_WORKAROUND_INACTIVE, + SYSNUM_WORKAROUND_PROCESS_FAULTY_CALL, + SYSNUM_WORKAROUND_PROCESS_REPLACED_CALL + } sysnum_workaround_state; + int suppressed_signal; + } chain; + + /* Load info generated during execve sysenter and used during + * execve sysexit. */ + struct load_info *load_info; + +#ifdef ARCH_ARM64 + bool is_aarch32; +#endif + + + /********************************************************************** + * Private but inherited resources * + **********************************************************************/ + + /* Verbose level. */ + int verbose; + + /* State of the seccomp acceleration for this tracee. */ + enum { DISABLED = 0, DISABLING, ENABLED } seccomp; + + /* Ensure the sysexit stage is always hit under seccomp. */ + bool sysexit_pending; + + /* If true, syscall entry was handled by seccomp and next SIGTRAP | 0x80 + * has to be ignored as it's same syscall entry */ + bool seccomp_already_handled_enter; + + /********************************************************************** + * Shared or private resources, depending on the CLONE_FS/VM flags. * + **********************************************************************/ + + /* Information related to a file-system name-space. */ + FileSystemNameSpace *fs; + + /* Virtual heap, emulated with a regular memory mapping. */ + Heap *heap; + + + /********************************************************************** + * Shared resources until the tracee makes a call to execve(). * + **********************************************************************/ + + /* Path to the executable, à la /proc/self/exe. */ + char *exe; + char *new_exe; + + + /********************************************************************** + * Shared or private resources, depending on the (re-)configuration * + **********************************************************************/ + + /* Path to glue between the guest rootfs and the host rootfs. */ + const char *glue; + +} Tracee; + +#define TRACEE(a) talloc_get_type_abort(talloc_parent(talloc_parent(a)), Tracee) + +extern Tracee *get_tracee(const Tracee *tracee, pid_t pid, bool create); +extern Tracee *get_stopped_ptracee(const Tracee *ptracer, pid_t pid, + bool only_with_pevent, word_t wait_options); +extern bool has_ptracees(const Tracee *ptracer, pid_t pid, word_t wait_options); +extern int new_child(Tracee *parent, word_t clone_flags); +extern Tracee *new_dummy_tracee(TALLOC_CTX *context); +extern void terminate_tracee(Tracee *tracee); +extern void free_terminated_tracees(); +extern void kill_all_tracees(); + +typedef LIST_HEAD(tracees, tracee) Tracees; +extern Tracees *get_tracees_list_head(); + +#endif /* TRACEE_H */ diff --git a/app/src/main/cpp/proot/talloc/talloc.c b/app/src/main/cpp/proot/talloc/talloc.c new file mode 100644 index 0000000..11ff9e5 --- /dev/null +++ b/app/src/main/cpp/proot/talloc/talloc.c @@ -0,0 +1,3013 @@ +/* + Samba Unix SMB/CIFS implementation. + + Samba trivial allocation library - new interface + + NOTE: Please read talloc_guide.txt for full documentation + + Copyright (C) Andrew Tridgell 2004 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +/* + inspired by http://swapped.cc/halloc/ +*/ + +#include "talloc.h" + +#ifdef HAVE_SYS_AUXV_H +#include +#endif + +/* Special macros that are no-ops except when run under Valgrind on + * x86. They've moved a little bit from valgrind 1.0.4 to 1.9.4 */ +#ifdef HAVE_VALGRIND_MEMCHECK_H + /* memcheck.h includes valgrind.h */ +#include +#elif defined(HAVE_VALGRIND_H) +#include +#endif + +/* use this to force every realloc to change the pointer, to stress test + code that might not cope */ +#define ALWAYS_REALLOC 0 + +#define TALLOC_BUILD_VERSION_MINOR 1 +#define TALLOC_BUILD_VERSION_MAJOR 2 +#define TALLOC_BUILD_VERSION_RELEASE 14 + +#define _PUBLIC_ __attribute__((visibility("default"))) + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +#define MAX_TALLOC_SIZE 0x10000000 + +#define TALLOC_FLAG_FREE 0x01 +#define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ +#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ + +/* + * Bits above this are random, used to make it harder to fake talloc + * headers during an attack. Try not to change this without good reason. + */ +#define TALLOC_FLAG_MASK 0x0F + +#define TALLOC_MAGIC_REFERENCE ((const char *)1) + +#define TALLOC_MAGIC_BASE 0xe814ec70 +#define TALLOC_MAGIC_NON_RANDOM ( \ + ~TALLOC_FLAG_MASK & ( \ + TALLOC_MAGIC_BASE + \ + (TALLOC_BUILD_VERSION_MAJOR << 24) + \ + (TALLOC_BUILD_VERSION_MINOR << 16) + \ + (TALLOC_BUILD_VERSION_RELEASE << 8))) +static unsigned int talloc_magic = TALLOC_MAGIC_NON_RANDOM; + +/* by default we abort when given a bad pointer (such as when talloc_free() is called + on a pointer that came from malloc() */ +#ifndef TALLOC_ABORT +#define TALLOC_ABORT(reason) abort() +#endif + +#ifndef discard_const_p +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) +# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) +#else +# define discard_const_p(type, ptr) ((type *)(ptr)) +#endif +#endif + +/* these macros gain us a few percent of speed on gcc */ +#if (__GNUC__ >= 3) +/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 + as its first argument */ +#ifndef likely +#define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif +#else +#ifndef likely +#define likely(x) (x) +#endif +#ifndef unlikely +#define unlikely(x) (x) +#endif +#endif + +/* this null_context is only used if talloc_enable_leak_report() or + talloc_enable_leak_report_full() is called, otherwise it remains + NULL +*/ +static void *null_context; +static bool talloc_report_null; +static bool talloc_report_null_full; +static void *autofree_context; + +static void talloc_setup_atexit(void); + +/* used to enable fill of memory on free, which can be useful for + * catching use after free errors when valgrind is too slow + */ +static struct { + bool initialised; + bool enabled; + uint8_t fill_value; +} talloc_fill; + +#define TALLOC_FILL_ENV "TALLOC_FREE_FILL" + +/* + * do not wipe the header, to allow the + * double-free logic to still work + */ +#define TC_INVALIDATE_FULL_FILL_CHUNK(_tc) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = (_tc)->size; \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) +/* Mark the whole chunk as not accessable */ +#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { \ + size_t _flen = TC_HDR_SIZE + (_tc)->size; \ + char *_fptr = (char *)(_tc); \ + VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \ +} while(0) +#else +#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { } while (0) +#endif + +#define TC_INVALIDATE_FULL_CHUNK(_tc) do { \ + TC_INVALIDATE_FULL_FILL_CHUNK(_tc); \ + TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc); \ +} while (0) + +#define TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) +/* Mark the unused bytes not accessable */ +#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \ +} while (0) +#else +#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0) +#endif + +#define TC_INVALIDATE_SHRINK_CHUNK(_tc, _new_size) do { \ + TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size); \ + TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \ +} while (0) + +#define TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \ + if (unlikely(talloc_fill.enabled)) { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + memset(_fptr, talloc_fill.fill_value, _flen); \ + } \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) +/* Mark the unused bytes as undefined */ +#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \ + size_t _flen = (_tc)->size - (_new_size); \ + char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \ + _fptr += (_new_size); \ + VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \ +} while (0) +#else +#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0) +#endif + +#define TC_UNDEFINE_SHRINK_CHUNK(_tc, _new_size) do { \ + TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size); \ + TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \ +} while (0) + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) +/* Mark the new bytes as undefined */ +#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { \ + size_t _old_used = TC_HDR_SIZE + (_tc)->size; \ + size_t _new_used = TC_HDR_SIZE + (_new_size); \ + size_t _flen = _new_used - _old_used; \ + char *_fptr = _old_used + (char *)(_tc); \ + VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \ +} while (0) +#else +#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { } while (0) +#endif + +#define TC_UNDEFINE_GROW_CHUNK(_tc, _new_size) do { \ + TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size); \ +} while (0) + +struct talloc_reference_handle { + struct talloc_reference_handle *next, *prev; + void *ptr; + const char *location; +}; + +struct talloc_memlimit { + struct talloc_chunk *parent; + struct talloc_memlimit *upper; + size_t max_size; + size_t cur_size; +}; + +static inline bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size); +static inline void talloc_memlimit_grow(struct talloc_memlimit *limit, + size_t size); +static inline void talloc_memlimit_shrink(struct talloc_memlimit *limit, + size_t size); +static inline void tc_memlimit_update_on_free(struct talloc_chunk *tc); + +static inline void _tc_set_name_const(struct talloc_chunk *tc, + const char *name); +static struct talloc_chunk *_vasprintf_tc(const void *t, + const char *fmt, + va_list ap); + +typedef int (*talloc_destructor_t)(void *); + +struct talloc_pool_hdr; + +struct talloc_chunk { + /* + * flags includes the talloc magic, which is randomised to + * make overwrite attacks harder + */ + unsigned flags; + + /* + * If you have a logical tree like: + * + * + * / | \ + * / | \ + * / | \ + * + * + * The actual talloc tree is: + * + * + * | + * - - + * + * The children are linked with next/prev pointers, and + * child 1 is linked to the parent with parent/child + * pointers. + */ + + struct talloc_chunk *next, *prev; + struct talloc_chunk *parent, *child; + struct talloc_reference_handle *refs; + talloc_destructor_t destructor; + const char *name; + size_t size; + + /* + * limit semantics: + * if 'limit' is set it means all *new* children of the context will + * be limited to a total aggregate size ox max_size for memory + * allocations. + * cur_size is used to keep track of the current use + */ + struct talloc_memlimit *limit; + + /* + * For members of a pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" + * is a pointer to the struct talloc_chunk of the pool that it was + * allocated from. This way children can quickly find the pool to chew + * from. + */ + struct talloc_pool_hdr *pool; +}; + +/* 16 byte alignment seems to keep everyone happy */ +#define TC_ALIGN16(s) (((s)+15)&~15) +#define TC_HDR_SIZE TC_ALIGN16(sizeof(struct talloc_chunk)) +#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) + +_PUBLIC_ int talloc_version_major(void) +{ + return TALLOC_VERSION_MAJOR; +} + +_PUBLIC_ int talloc_version_minor(void) +{ + return TALLOC_VERSION_MINOR; +} + +_PUBLIC_ int talloc_test_get_magic(void) +{ + return talloc_magic; +} + +static inline void _talloc_chunk_set_free(struct talloc_chunk *tc, + const char *location) +{ + /* + * Mark this memory as free, and also over-stamp the talloc + * magic with the old-style magic. + * + * Why? This tries to avoid a memory read use-after-free from + * disclosing our talloc magic, which would then allow an + * attacker to prepare a valid header and so run a destructor. + * + */ + tc->flags = TALLOC_MAGIC_NON_RANDOM | TALLOC_FLAG_FREE + | (tc->flags & TALLOC_FLAG_MASK); + + /* we mark the freed memory with where we called the free + * from. This means on a double free error we can report where + * the first free came from + */ + if (location) { + tc->name = location; + } +} + +static inline void _talloc_chunk_set_not_free(struct talloc_chunk *tc) +{ + /* + * Mark this memory as not free. + * + * Why? This is memory either in a pool (and so available for + * talloc's re-use or after the realloc(). We need to mark + * the memory as free() before any realloc() call as we can't + * write to the memory after that. + * + * We put back the normal magic instead of the 'not random' + * magic. + */ + + tc->flags = talloc_magic | + ((tc->flags & TALLOC_FLAG_MASK) & ~TALLOC_FLAG_FREE); +} + +static void (*talloc_log_fn)(const char *message); + +_PUBLIC_ void talloc_set_log_fn(void (*log_fn)(const char *message)) +{ + talloc_log_fn = log_fn; +} + +void talloc_lib_init(void) __attribute__((constructor)); +void talloc_lib_init(void) +{ + uint32_t random_value; +#if defined(HAVE_GETAUXVAL) && defined(AT_RANDOM) + uint8_t *p; + /* + * Use the kernel-provided random values used for + * ASLR. This won't change per-exec, which is ideal for us + */ + p = (uint8_t *) getauxval(AT_RANDOM); + if (p) { + /* + * We get 16 bytes from getauxval. By calling rand(), + * a totally insecure PRNG, but one that will + * deterministically have a different value when called + * twice, we ensure that if two talloc-like libraries + * are somehow loaded in the same address space, that + * because we choose different bytes, we will keep the + * protection against collision of multiple talloc + * libs. + * + * This protection is important because the effects of + * passing a talloc pointer from one to the other may + * be very hard to determine. + */ + int offset = rand() % (16 - sizeof(random_value)); + memcpy(&random_value, p + offset, sizeof(random_value)); + } else +#endif + { + /* + * Otherwise, hope the location we are loaded in + * memory is randomised by someone else + */ + random_value = ((uintptr_t)talloc_lib_init & 0xFFFFFFFF); + } + talloc_magic = random_value & ~TALLOC_FLAG_MASK; +} + +static void talloc_lib_atexit(void) +{ + TALLOC_FREE(autofree_context); + + if (talloc_total_size(null_context) == 0) { + return; + } + + if (talloc_report_null_full) { + talloc_report_full(null_context, stderr); + } else if (talloc_report_null) { + talloc_report(null_context, stderr); + } +} + +static void talloc_setup_atexit(void) +{ + static bool done; + + if (done) { + return; + } + + atexit(talloc_lib_atexit); + done = true; +} + +static void talloc_log(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); +static void talloc_log(const char *fmt, ...) +{ + va_list ap; + char *message; + + if (!talloc_log_fn) { + return; + } + + va_start(ap, fmt); + message = talloc_vasprintf(NULL, fmt, ap); + va_end(ap); + + talloc_log_fn(message); + talloc_free(message); +} + +static void talloc_log_stderr(const char *message) +{ + fprintf(stderr, "%s", message); +} + +_PUBLIC_ void talloc_set_log_stderr(void) +{ + talloc_set_log_fn(talloc_log_stderr); +} + +static void (*talloc_abort_fn)(const char *reason); + +_PUBLIC_ void talloc_set_abort_fn(void (*abort_fn)(const char *reason)) +{ + talloc_abort_fn = abort_fn; +} + +static void talloc_abort(const char *reason) +{ + talloc_log("%s\n", reason); + + if (!talloc_abort_fn) { + TALLOC_ABORT(reason); + } + + talloc_abort_fn(reason); +} + +static void talloc_abort_access_after_free(void) +{ + talloc_abort("Bad talloc magic value - access after free"); +} + +static void talloc_abort_unknown_value(void) +{ + talloc_abort("Bad talloc magic value - unknown value"); +} + +/* panic if we get a bad magic value */ +static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) +{ + const char *pp = (const char *)ptr; + struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); + if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~TALLOC_FLAG_MASK)) != talloc_magic)) { + if ((tc->flags & (TALLOC_FLAG_FREE | ~TALLOC_FLAG_MASK)) + == (TALLOC_MAGIC_NON_RANDOM | TALLOC_FLAG_FREE)) { + talloc_log("talloc: access after free error - first free may be at %s\n", tc->name); + talloc_abort_access_after_free(); + return NULL; + } + + talloc_abort_unknown_value(); + return NULL; + } + return tc; +} + +/* hook into the front of the list */ +#define _TLIST_ADD(list, p) \ +do { \ + if (!(list)) { \ + (list) = (p); \ + (p)->next = (p)->prev = NULL; \ + } else { \ + (list)->prev = (p); \ + (p)->next = (list); \ + (p)->prev = NULL; \ + (list) = (p); \ + }\ +} while (0) + +/* remove an element from a list - element doesn't have to be in list. */ +#define _TLIST_REMOVE(list, p) \ +do { \ + if ((p) == (list)) { \ + (list) = (p)->next; \ + if (list) (list)->prev = NULL; \ + } else { \ + if ((p)->prev) (p)->prev->next = (p)->next; \ + if ((p)->next) (p)->next->prev = (p)->prev; \ + } \ + if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ +} while (0) + + +/* + return the parent chunk of a pointer +*/ +static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + while (tc->prev) tc=tc->prev; + + return tc->parent; +} + +_PUBLIC_ void *talloc_parent(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? TC_PTR_FROM_CHUNK(tc) : NULL; +} + +/* + find parents name +*/ +_PUBLIC_ const char *talloc_parent_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_parent_chunk(ptr); + return tc? tc->name : NULL; +} + +/* + A pool carries an in-pool object count count in the first 16 bytes. + bytes. This is done to support talloc_steal() to a parent outside of the + pool. The count includes the pool itself, so a talloc_free() on a pool will + only destroy the pool if the count has dropped to zero. A talloc_free() of a + pool member will reduce the count, and eventually also call free(3) on the + pool memory. + + The object count is not put into "struct talloc_chunk" because it is only + relevant for talloc pools and the alignment to 16 bytes would increase the + memory footprint of each talloc chunk by those 16 bytes. +*/ + +struct talloc_pool_hdr { + void *end; + unsigned int object_count; + size_t poolsize; +}; + +#define TP_HDR_SIZE TC_ALIGN16(sizeof(struct talloc_pool_hdr)) + +static inline struct talloc_pool_hdr *talloc_pool_from_chunk(struct talloc_chunk *c) +{ + return (struct talloc_pool_hdr *)((char *)c - TP_HDR_SIZE); +} + +static inline struct talloc_chunk *talloc_chunk_from_pool(struct talloc_pool_hdr *h) +{ + return (struct talloc_chunk *)((char *)h + TP_HDR_SIZE); +} + +static inline void *tc_pool_end(struct talloc_pool_hdr *pool_hdr) +{ + struct talloc_chunk *tc = talloc_chunk_from_pool(pool_hdr); + return (char *)tc + TC_HDR_SIZE + pool_hdr->poolsize; +} + +static inline size_t tc_pool_space_left(struct talloc_pool_hdr *pool_hdr) +{ + return (char *)tc_pool_end(pool_hdr) - (char *)pool_hdr->end; +} + +/* If tc is inside a pool, this gives the next neighbour. */ +static inline void *tc_next_chunk(struct talloc_chunk *tc) +{ + return (char *)tc + TC_ALIGN16(TC_HDR_SIZE + tc->size); +} + +static inline void *tc_pool_first_chunk(struct talloc_pool_hdr *pool_hdr) +{ + struct talloc_chunk *tc = talloc_chunk_from_pool(pool_hdr); + return tc_next_chunk(tc); +} + +/* Mark the whole remaining pool as not accessable */ +static inline void tc_invalidate_pool(struct talloc_pool_hdr *pool_hdr) +{ + size_t flen = tc_pool_space_left(pool_hdr); + + if (unlikely(talloc_fill.enabled)) { + memset(pool_hdr->end, talloc_fill.fill_value, flen); + } + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS(pool_hdr->end, flen); +#endif +} + +/* + Allocate from a pool +*/ + +static inline struct talloc_chunk *tc_alloc_pool(struct talloc_chunk *parent, + size_t size, size_t prefix_len) +{ + struct talloc_pool_hdr *pool_hdr = NULL; + size_t space_left; + struct talloc_chunk *result; + size_t chunk_size; + + if (parent == NULL) { + return NULL; + } + + if (parent->flags & TALLOC_FLAG_POOL) { + pool_hdr = talloc_pool_from_chunk(parent); + } + else if (parent->flags & TALLOC_FLAG_POOLMEM) { + pool_hdr = parent->pool; + } + + if (pool_hdr == NULL) { + return NULL; + } + + space_left = tc_pool_space_left(pool_hdr); + + /* + * Align size to 16 bytes + */ + chunk_size = TC_ALIGN16(size + prefix_len); + + if (space_left < chunk_size) { + return NULL; + } + + result = (struct talloc_chunk *)((char *)pool_hdr->end + prefix_len); + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + VALGRIND_MAKE_MEM_UNDEFINED(pool_hdr->end, chunk_size); +#endif + + pool_hdr->end = (void *)((char *)pool_hdr->end + chunk_size); + + result->flags = talloc_magic | TALLOC_FLAG_POOLMEM; + result->pool = pool_hdr; + + pool_hdr->object_count++; + + return result; +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +static inline void *__talloc_with_prefix(const void *context, + size_t size, + size_t prefix_len, + struct talloc_chunk **tc_ret) +{ + struct talloc_chunk *tc = NULL; + struct talloc_memlimit *limit = NULL; + size_t total_len = TC_HDR_SIZE + size + prefix_len; + struct talloc_chunk *parent = NULL; + + if (unlikely(context == NULL)) { + context = null_context; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + if (unlikely(total_len < TC_HDR_SIZE)) { + return NULL; + } + + if (likely(context != NULL)) { + parent = talloc_chunk_from_ptr(context); + + if (parent->limit != NULL) { + limit = parent->limit; + } + + tc = tc_alloc_pool(parent, TC_HDR_SIZE+size, prefix_len); + } + + if (tc == NULL) { + char *ptr; + + /* + * Only do the memlimit check/update on actual allocation. + */ + if (!talloc_memlimit_check(limit, total_len)) { + errno = ENOMEM; + return NULL; + } + + ptr = malloc(total_len); + if (unlikely(ptr == NULL)) { + return NULL; + } + tc = (struct talloc_chunk *)(ptr + prefix_len); + tc->flags = talloc_magic; + tc->pool = NULL; + + talloc_memlimit_grow(limit, total_len); + } + + tc->limit = limit; + tc->size = size; + tc->destructor = NULL; + tc->child = NULL; + tc->name = NULL; + tc->refs = NULL; + + if (likely(context != NULL)) { + if (parent->child) { + parent->child->parent = NULL; + tc->next = parent->child; + tc->next->prev = tc; + } else { + tc->next = NULL; + } + tc->parent = parent; + tc->prev = NULL; + parent->child = tc; + } else { + tc->next = tc->prev = tc->parent = NULL; + } + + *tc_ret = tc; + return TC_PTR_FROM_CHUNK(tc); +} + +static inline void *__talloc(const void *context, + size_t size, + struct talloc_chunk **tc) +{ + return __talloc_with_prefix(context, size, 0, tc); +} + +/* + * Create a talloc pool + */ + +static inline void *_talloc_pool(const void *context, size_t size) +{ + struct talloc_chunk *tc; + struct talloc_pool_hdr *pool_hdr; + void *result; + + result = __talloc_with_prefix(context, size, TP_HDR_SIZE, &tc); + + if (unlikely(result == NULL)) { + return NULL; + } + + pool_hdr = talloc_pool_from_chunk(tc); + + tc->flags |= TALLOC_FLAG_POOL; + tc->size = 0; + + pool_hdr->object_count = 1; + pool_hdr->end = result; + pool_hdr->poolsize = size; + + tc_invalidate_pool(pool_hdr); + + return result; +} + +_PUBLIC_ void *talloc_pool(const void *context, size_t size) +{ + return _talloc_pool(context, size); +} + +/* + * Create a talloc pool correctly sized for a basic size plus + * a number of subobjects whose total size is given. Essentially + * a custom allocator for talloc to reduce fragmentation. + */ + +_PUBLIC_ void *_talloc_pooled_object(const void *ctx, + size_t type_size, + const char *type_name, + unsigned num_subobjects, + size_t total_subobjects_size) +{ + size_t poolsize, subobjects_slack, tmp; + struct talloc_chunk *tc; + struct talloc_pool_hdr *pool_hdr; + void *ret; + + poolsize = type_size + total_subobjects_size; + + if ((poolsize < type_size) || (poolsize < total_subobjects_size)) { + goto overflow; + } + + if (num_subobjects == UINT_MAX) { + goto overflow; + } + num_subobjects += 1; /* the object body itself */ + + /* + * Alignment can increase the pool size by at most 15 bytes per object + * plus alignment for the object itself + */ + subobjects_slack = (TC_HDR_SIZE + TP_HDR_SIZE + 15) * num_subobjects; + if (subobjects_slack < num_subobjects) { + goto overflow; + } + + tmp = poolsize + subobjects_slack; + if ((tmp < poolsize) || (tmp < subobjects_slack)) { + goto overflow; + } + poolsize = tmp; + + ret = _talloc_pool(ctx, poolsize); + if (ret == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ret); + tc->size = type_size; + + pool_hdr = talloc_pool_from_chunk(tc); + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + VALGRIND_MAKE_MEM_UNDEFINED(pool_hdr->end, type_size); +#endif + + pool_hdr->end = ((char *)pool_hdr->end + TC_ALIGN16(type_size)); + + _tc_set_name_const(tc, type_name); + return ret; + +overflow: + return NULL; +} + +/* + setup a destructor to be called on free of a pointer + the destructor should return 0 on success, or -1 on failure. + if the destructor fails then the free is failed, and the memory can + be continued to be used +*/ +_PUBLIC_ void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + tc->destructor = destructor; +} + +/* + increase the reference count on a piece of memory. +*/ +_PUBLIC_ int talloc_increase_ref_count(const void *ptr) +{ + if (unlikely(!talloc_reference(null_context, ptr))) { + return -1; + } + return 0; +} + +/* + helper for talloc_reference() + + this is referenced by a function pointer and should not be inline +*/ +static int talloc_reference_destructor(struct talloc_reference_handle *handle) +{ + struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); + _TLIST_REMOVE(ptr_tc->refs, handle); + return 0; +} + +/* + more efficient way to add a name to a pointer - the name must point to a + true string constant +*/ +static inline void _tc_set_name_const(struct talloc_chunk *tc, + const char *name) +{ + tc->name = name; +} + +/* + internal talloc_named_const() +*/ +static inline void *_talloc_named_const(const void *context, size_t size, const char *name) +{ + void *ptr; + struct talloc_chunk *tc; + + ptr = __talloc(context, size, &tc); + if (unlikely(ptr == NULL)) { + return NULL; + } + + _tc_set_name_const(tc, name); + + return ptr; +} + +/* + make a secondary reference to a pointer, hanging off the given context. + the pointer remains valid until both the original caller and this given + context are freed. + + the major use for this is when two different structures need to reference the + same underlying data, and you want to be able to free the two instances separately, + and in either order +*/ +_PUBLIC_ void *_talloc_reference_loc(const void *context, const void *ptr, const char *location) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *handle; + if (unlikely(ptr == NULL)) return NULL; + + tc = talloc_chunk_from_ptr(ptr); + handle = (struct talloc_reference_handle *)_talloc_named_const(context, + sizeof(struct talloc_reference_handle), + TALLOC_MAGIC_REFERENCE); + if (unlikely(handle == NULL)) return NULL; + + /* note that we hang the destructor off the handle, not the + main context as that allows the caller to still setup their + own destructor on the context if they want to */ + talloc_set_destructor(handle, talloc_reference_destructor); + handle->ptr = discard_const_p(void, ptr); + handle->location = location; + _TLIST_ADD(tc->refs, handle); + return handle->ptr; +} + +static void *_talloc_steal_internal(const void *new_ctx, const void *ptr); + +static inline void _tc_free_poolmem(struct talloc_chunk *tc, + const char *location) +{ + struct talloc_pool_hdr *pool; + struct talloc_chunk *pool_tc; + void *next_tc; + + pool = tc->pool; + pool_tc = talloc_chunk_from_pool(pool); + next_tc = tc_next_chunk(tc); + + _talloc_chunk_set_free(tc, location); + + TC_INVALIDATE_FULL_CHUNK(tc); + + if (unlikely(pool->object_count == 0)) { + talloc_abort("Pool object count zero!"); + return; + } + + pool->object_count--; + + if (unlikely(pool->object_count == 1 + && !(pool_tc->flags & TALLOC_FLAG_FREE))) { + /* + * if there is just one object left in the pool + * and pool->flags does not have TALLOC_FLAG_FREE, + * it means this is the pool itself and + * the rest is available for new objects + * again. + */ + pool->end = tc_pool_first_chunk(pool); + tc_invalidate_pool(pool); + return; + } + + if (unlikely(pool->object_count == 0)) { + /* + * we mark the freed memory with where we called the free + * from. This means on a double free error we can report where + * the first free came from + */ + pool_tc->name = location; + + if (pool_tc->flags & TALLOC_FLAG_POOLMEM) { + _tc_free_poolmem(pool_tc, location); + } else { + /* + * The tc_memlimit_update_on_free() + * call takes into account the + * prefix TP_HDR_SIZE allocated before + * the pool talloc_chunk. + */ + tc_memlimit_update_on_free(pool_tc); + TC_INVALIDATE_FULL_CHUNK(pool_tc); + free(pool); + } + return; + } + + if (pool->end == next_tc) { + /* + * if pool->pool still points to end of + * 'tc' (which is stored in the 'next_tc' variable), + * we can reclaim the memory of 'tc'. + */ + pool->end = tc; + return; + } + + /* + * Do nothing. The memory is just "wasted", waiting for the pool + * itself to be freed. + */ +} + +static inline void _tc_free_children_internal(struct talloc_chunk *tc, + void *ptr, + const char *location); + +static inline int _talloc_free_internal(void *ptr, const char *location); + +/* + internal free call that takes a struct talloc_chunk *. +*/ +static inline int _tc_free_internal(struct talloc_chunk *tc, + const char *location) +{ + void *ptr_to_free; + void *ptr = TC_PTR_FROM_CHUNK(tc); + + if (unlikely(tc->refs)) { + int is_child; + /* check if this is a reference from a child or + * grandchild back to it's parent or grandparent + * + * in that case we need to remove the reference and + * call another instance of talloc_free() on the current + * pointer. + */ + is_child = talloc_is_parent(tc->refs, ptr); + _talloc_free_internal(tc->refs, location); + if (is_child) { + return _talloc_free_internal(ptr, location); + } + return -1; + } + + if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { + /* we have a free loop - stop looping */ + return 0; + } + + if (unlikely(tc->destructor)) { + talloc_destructor_t d = tc->destructor; + + /* + * Protect the destructor against some overwrite + * attacks, by explicitly checking it has the right + * magic here. + */ + if (talloc_chunk_from_ptr(ptr) != tc) { + /* + * This can't actually happen, the + * call itself will panic. + */ + TALLOC_ABORT("talloc_chunk_from_ptr failed!"); + } + + if (d == (talloc_destructor_t)-1) { + return -1; + } + tc->destructor = (talloc_destructor_t)-1; + if (d(ptr) == -1) { + /* + * Only replace the destructor pointer if + * calling the destructor didn't modify it. + */ + if (tc->destructor == (talloc_destructor_t)-1) { + tc->destructor = d; + } + return -1; + } + tc->destructor = NULL; + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + tc->prev = tc->next = NULL; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + _tc_free_children_internal(tc, ptr, location); + + _talloc_chunk_set_free(tc, location); + + if (tc->flags & TALLOC_FLAG_POOL) { + struct talloc_pool_hdr *pool; + + pool = talloc_pool_from_chunk(tc); + + if (unlikely(pool->object_count == 0)) { + talloc_abort("Pool object count zero!"); + return 0; + } + + pool->object_count--; + + if (likely(pool->object_count != 0)) { + return 0; + } + + /* + * With object_count==0, a pool becomes a normal piece of + * memory to free. If it's allocated inside a pool, it needs + * to be freed as poolmem, else it needs to be just freed. + */ + ptr_to_free = pool; + } else { + ptr_to_free = tc; + } + + if (tc->flags & TALLOC_FLAG_POOLMEM) { + _tc_free_poolmem(tc, location); + return 0; + } + + tc_memlimit_update_on_free(tc); + + TC_INVALIDATE_FULL_CHUNK(tc); + free(ptr_to_free); + return 0; +} + +/* + internal talloc_free call +*/ +static inline int _talloc_free_internal(void *ptr, const char *location) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + /* possibly initialised the talloc fill value */ + if (unlikely(!talloc_fill.initialised)) { + const char *fill = getenv(TALLOC_FILL_ENV); + if (fill != NULL) { + talloc_fill.enabled = true; + talloc_fill.fill_value = strtoul(fill, NULL, 0); + } + talloc_fill.initialised = true; + } + + tc = talloc_chunk_from_ptr(ptr); + return _tc_free_internal(tc, location); +} + +static inline size_t _talloc_total_limit_size(const void *ptr, + struct talloc_memlimit *old_limit, + struct talloc_memlimit *new_limit); + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +static void *_talloc_steal_internal(const void *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *new_tc; + size_t ctx_size = 0; + + if (unlikely(!ptr)) { + return NULL; + } + + if (unlikely(new_ctx == NULL)) { + new_ctx = null_context; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->limit != NULL) { + + ctx_size = _talloc_total_limit_size(ptr, NULL, NULL); + + /* Decrement the memory limit from the source .. */ + talloc_memlimit_shrink(tc->limit->upper, ctx_size); + + if (tc->limit->parent == tc) { + tc->limit->upper = NULL; + } else { + tc->limit = NULL; + } + } + + if (unlikely(new_ctx == NULL)) { + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + } + + tc->parent = tc->next = tc->prev = NULL; + return discard_const_p(void, ptr); + } + + new_tc = talloc_chunk_from_ptr(new_ctx); + + if (unlikely(tc == new_tc || tc->parent == new_tc)) { + return discard_const_p(void, ptr); + } + + if (tc->parent) { + _TLIST_REMOVE(tc->parent->child, tc); + if (tc->parent->child) { + tc->parent->child->parent = tc->parent; + } + } else { + if (tc->prev) tc->prev->next = tc->next; + if (tc->next) tc->next->prev = tc->prev; + tc->prev = tc->next = NULL; + } + + tc->parent = new_tc; + if (new_tc->child) new_tc->child->parent = NULL; + _TLIST_ADD(new_tc->child, tc); + + if (tc->limit || new_tc->limit) { + ctx_size = _talloc_total_limit_size(ptr, tc->limit, + new_tc->limit); + /* .. and increment it in the destination. */ + if (new_tc->limit) { + talloc_memlimit_grow(new_tc->limit, ctx_size); + } + } + + return discard_const_p(void, ptr); +} + +/* + move a lump of memory from one talloc context to another return the + ptr on success, or NULL if it could not be transferred. + passing NULL as ptr will always return NULL with no side effects. +*/ +_PUBLIC_ void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs != NULL) && talloc_parent(ptr) != new_ctx) { + struct talloc_reference_handle *h; + + talloc_log("WARNING: talloc_steal with references at %s\n", + location); + + for (h=tc->refs; h; h=h->next) { + talloc_log("\treference at %s\n", + h->location); + } + } + +#if 0 + /* this test is probably too expensive to have on in the + normal build, but it useful for debugging */ + if (talloc_is_parent(new_ctx, ptr)) { + talloc_log("WARNING: stealing into talloc child at %s\n", location); + } +#endif + + return _talloc_steal_internal(new_ctx, ptr); +} + +/* + this is like a talloc_steal(), but you must supply the old + parent. This resolves the ambiguity in a talloc_steal() which is + called on a context that has more than one parent (via references) + + The old parent can be either a reference or a parent +*/ +_PUBLIC_ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr) +{ + struct talloc_chunk *tc; + struct talloc_reference_handle *h; + + if (unlikely(ptr == NULL)) { + return NULL; + } + + if (old_parent == talloc_parent(ptr)) { + return _talloc_steal_internal(new_parent, ptr); + } + + tc = talloc_chunk_from_ptr(ptr); + for (h=tc->refs;h;h=h->next) { + if (talloc_parent(h) == old_parent) { + if (_talloc_steal_internal(new_parent, h) != h) { + return NULL; + } + return discard_const_p(void, ptr); + } + } + + /* it wasn't a parent */ + return NULL; +} + +/* + remove a secondary reference to a pointer. This undo's what + talloc_reference() has done. The context and pointer arguments + must match those given to a talloc_reference() +*/ +static inline int talloc_unreference(const void *context, const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + + if (unlikely(context == NULL)) { + context = null_context; + } + + for (h=tc->refs;h;h=h->next) { + struct talloc_chunk *p = talloc_parent_chunk(h); + if (p == NULL) { + if (context == NULL) break; + } else if (TC_PTR_FROM_CHUNK(p) == context) { + break; + } + } + if (h == NULL) { + return -1; + } + + return _talloc_free_internal(h, __location__); +} + +/* + remove a specific parent context from a pointer. This is a more + controlled variant of talloc_free() +*/ +_PUBLIC_ int talloc_unlink(const void *context, void *ptr) +{ + struct talloc_chunk *tc_p, *new_p, *tc_c; + void *new_parent; + + if (ptr == NULL) { + return -1; + } + + if (context == NULL) { + context = null_context; + } + + if (talloc_unreference(context, ptr) == 0) { + return 0; + } + + if (context != NULL) { + tc_c = talloc_chunk_from_ptr(context); + } else { + tc_c = NULL; + } + if (tc_c != talloc_parent_chunk(ptr)) { + return -1; + } + + tc_p = talloc_chunk_from_ptr(ptr); + + if (tc_p->refs == NULL) { + return _talloc_free_internal(ptr, __location__); + } + + new_p = talloc_parent_chunk(tc_p->refs); + if (new_p) { + new_parent = TC_PTR_FROM_CHUNK(new_p); + } else { + new_parent = NULL; + } + + if (talloc_unreference(new_parent, ptr) != 0) { + return -1; + } + + _talloc_steal_internal(new_parent, ptr); + + return 0; +} + +/* + add a name to an existing pointer - va_list version +*/ +static inline const char *tc_set_name_v(struct talloc_chunk *tc, + const char *fmt, + va_list ap) PRINTF_ATTRIBUTE(2,0); + +static inline const char *tc_set_name_v(struct talloc_chunk *tc, + const char *fmt, + va_list ap) +{ + struct talloc_chunk *name_tc = _vasprintf_tc(TC_PTR_FROM_CHUNK(tc), + fmt, + ap); + if (likely(name_tc)) { + tc->name = TC_PTR_FROM_CHUNK(name_tc); + _tc_set_name_const(name_tc, ".name"); + } else { + tc->name = NULL; + } + return tc->name; +} + +/* + add a name to an existing pointer +*/ +_PUBLIC_ const char *talloc_set_name(const void *ptr, const char *fmt, ...) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + const char *name; + va_list ap; + va_start(ap, fmt); + name = tc_set_name_v(tc, fmt, ap); + va_end(ap); + return name; +} + + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +_PUBLIC_ void *talloc_named(const void *context, size_t size, const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + struct talloc_chunk *tc; + + ptr = __talloc(context, size, &tc); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = tc_set_name_v(tc, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free_internal(ptr, __location__); + return NULL; + } + + return ptr; +} + +/* + return the name of a talloc ptr, or "UNNAMED" +*/ +static inline const char *__talloc_get_name(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { + return ".reference"; + } + if (likely(tc->name)) { + return tc->name; + } + return "UNNAMED"; +} + +_PUBLIC_ const char *talloc_get_name(const void *ptr) +{ + return __talloc_get_name(ptr); +} + +/* + check if a pointer has the given name. If it does, return the pointer, + otherwise return NULL +*/ +_PUBLIC_ void *talloc_check_name(const void *ptr, const char *name) +{ + const char *pname; + if (unlikely(ptr == NULL)) return NULL; + pname = __talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + return NULL; +} + +static void talloc_abort_type_mismatch(const char *location, + const char *name, + const char *expected) +{ + const char *reason; + + reason = talloc_asprintf(NULL, + "%s: Type mismatch: name[%s] expected[%s]", + location, + name?name:"NULL", + expected); + if (!reason) { + reason = "Type mismatch"; + } + + talloc_abort(reason); +} + +_PUBLIC_ void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location) +{ + const char *pname; + + if (unlikely(ptr == NULL)) { + talloc_abort_type_mismatch(location, NULL, name); + return NULL; + } + + pname = __talloc_get_name(ptr); + if (likely(pname == name || strcmp(pname, name) == 0)) { + return discard_const_p(void, ptr); + } + + talloc_abort_type_mismatch(location, pname, name); + return NULL; +} + +/* + this is for compatibility with older versions of talloc +*/ +_PUBLIC_ void *talloc_init(const char *fmt, ...) +{ + va_list ap; + void *ptr; + const char *name; + struct talloc_chunk *tc; + + ptr = __talloc(NULL, 0, &tc); + if (unlikely(ptr == NULL)) return NULL; + + va_start(ap, fmt); + name = tc_set_name_v(tc, fmt, ap); + va_end(ap); + + if (unlikely(name == NULL)) { + _talloc_free_internal(ptr, __location__); + return NULL; + } + + return ptr; +} + +static inline void _tc_free_children_internal(struct talloc_chunk *tc, + void *ptr, + const char *location) +{ + while (tc->child) { + /* we need to work out who will own an abandoned child + if it cannot be freed. In priority order, the first + choice is owner of any remaining reference to this + pointer, the second choice is our parent, and the + final choice is the null context. */ + void *child = TC_PTR_FROM_CHUNK(tc->child); + const void *new_parent = null_context; + if (unlikely(tc->child->refs)) { + struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + if (unlikely(_tc_free_internal(tc->child, location) == -1)) { + if (talloc_parent_chunk(child) != tc) { + /* + * Destructor already reparented this child. + * No further reparenting needed. + */ + continue; + } + if (new_parent == null_context) { + struct talloc_chunk *p = talloc_parent_chunk(ptr); + if (p) new_parent = TC_PTR_FROM_CHUNK(p); + } + _talloc_steal_internal(new_parent, child); + } + } +} + +/* + this is a replacement for the Samba3 talloc_destroy_pool functionality. It + should probably not be used in new code. It's in here to keep the talloc + code consistent across Samba 3 and 4. +*/ +_PUBLIC_ void talloc_free_children(void *ptr) +{ + struct talloc_chunk *tc_name = NULL; + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return; + } + + tc = talloc_chunk_from_ptr(ptr); + + /* we do not want to free the context name if it is a child .. */ + if (likely(tc->child)) { + for (tc_name = tc->child; tc_name; tc_name = tc_name->next) { + if (tc->name == TC_PTR_FROM_CHUNK(tc_name)) break; + } + if (tc_name) { + _TLIST_REMOVE(tc->child, tc_name); + if (tc->child) { + tc->child->parent = tc; + } + } + } + + _tc_free_children_internal(tc, ptr, __location__); + + /* .. so we put it back after all other children have been freed */ + if (tc_name) { + if (tc->child) { + tc->child->parent = NULL; + } + tc_name->parent = tc; + _TLIST_ADD(tc->child, tc_name); + } +} + +/* + Allocate a bit of memory as a child of an existing pointer +*/ +_PUBLIC_ void *_talloc(const void *context, size_t size) +{ + struct talloc_chunk *tc; + return __talloc(context, size, &tc); +} + +/* + externally callable talloc_set_name_const() +*/ +_PUBLIC_ void talloc_set_name_const(const void *ptr, const char *name) +{ + _tc_set_name_const(talloc_chunk_from_ptr(ptr), name); +} + +/* + create a named talloc pointer. Any talloc pointer can be named, and + talloc_named() operates just like talloc() except that it allows you + to name the pointer. +*/ +_PUBLIC_ void *talloc_named_const(const void *context, size_t size, const char *name) +{ + return _talloc_named_const(context, size, name); +} + +/* + free a talloc pointer. This also frees all child pointers of this + pointer recursively + + return 0 if the memory is actually freed, otherwise -1. The memory + will not be freed if the ref_count is > 1 or the destructor (if + any) returns non-zero +*/ +_PUBLIC_ int _talloc_free(void *ptr, const char *location) +{ + struct talloc_chunk *tc; + + if (unlikely(ptr == NULL)) { + return -1; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (unlikely(tc->refs != NULL)) { + struct talloc_reference_handle *h; + + if (talloc_parent(ptr) == null_context && tc->refs->next == NULL) { + /* in this case we do know which parent should + get this pointer, as there is really only + one parent */ + return talloc_unlink(null_context, ptr); + } + + talloc_log("ERROR: talloc_free with references at %s\n", + location); + + for (h=tc->refs; h; h=h->next) { + talloc_log("\treference at %s\n", + h->location); + } + return -1; + } + + return _talloc_free_internal(ptr, location); +} + + + +/* + A talloc version of realloc. The context argument is only used if + ptr is NULL +*/ +_PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) +{ + struct talloc_chunk *tc; + void *new_ptr; + bool malloced = false; + struct talloc_pool_hdr *pool_hdr = NULL; + size_t old_size = 0; + size_t new_size = 0; + + /* size zero is equivalent to free() */ + if (unlikely(size == 0)) { + talloc_unlink(context, ptr); + return NULL; + } + + if (unlikely(size >= MAX_TALLOC_SIZE)) { + return NULL; + } + + /* realloc(NULL) is equivalent to malloc() */ + if (ptr == NULL) { + return _talloc_named_const(context, size, name); + } + + tc = talloc_chunk_from_ptr(ptr); + + /* don't allow realloc on referenced pointers */ + if (unlikely(tc->refs)) { + return NULL; + } + + /* don't let anybody try to realloc a talloc_pool */ + if (unlikely(tc->flags & TALLOC_FLAG_POOL)) { + return NULL; + } + + if (tc->limit && (size > tc->size)) { + if (!talloc_memlimit_check(tc->limit, (size - tc->size))) { + errno = ENOMEM; + return NULL; + } + } + + /* handle realloc inside a talloc_pool */ + if (unlikely(tc->flags & TALLOC_FLAG_POOLMEM)) { + pool_hdr = tc->pool; + } + +#if (ALWAYS_REALLOC == 0) + /* don't shrink if we have less than 1k to gain */ + if (size < tc->size && tc->limit == NULL) { + if (pool_hdr) { + void *next_tc = tc_next_chunk(tc); + TC_INVALIDATE_SHRINK_CHUNK(tc, size); + tc->size = size; + if (next_tc == pool_hdr->end) { + /* note: tc->size has changed, so this works */ + pool_hdr->end = tc_next_chunk(tc); + } + return ptr; + } else if ((tc->size - size) < 1024) { + /* + * if we call TC_INVALIDATE_SHRINK_CHUNK() here + * we would need to call TC_UNDEFINE_GROW_CHUNK() + * after each realloc call, which slows down + * testing a lot :-(. + * + * That is why we only mark memory as undefined here. + */ + TC_UNDEFINE_SHRINK_CHUNK(tc, size); + + /* do not shrink if we have less than 1k to gain */ + tc->size = size; + return ptr; + } + } else if (tc->size == size) { + /* + * do not change the pointer if it is exactly + * the same size. + */ + return ptr; + } +#endif + + /* + * by resetting magic we catch users of the old memory + * + * We mark this memory as free, and also over-stamp the talloc + * magic with the old-style magic. + * + * Why? This tries to avoid a memory read use-after-free from + * disclosing our talloc magic, which would then allow an + * attacker to prepare a valid header and so run a destructor. + * + * What else? We have to re-stamp back a valid normal magic + * on this memory once realloc() is done, as it will have done + * a memcpy() into the new valid memory. We can't do this in + * reverse as that would be a real use-after-free. + */ + _talloc_chunk_set_free(tc, NULL); + +#if ALWAYS_REALLOC + if (pool_hdr) { + new_ptr = tc_alloc_pool(tc, size + TC_HDR_SIZE, 0); + pool_hdr->object_count--; + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + new_size = size; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + TC_INVALIDATE_FULL_CHUNK(tc); + } + } else { + /* We're doing malloc then free here, so record the difference. */ + old_size = tc->size; + new_size = size; + new_ptr = malloc(size + TC_HDR_SIZE); + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE); + free(tc); + } + } +#else + if (pool_hdr) { + struct talloc_chunk *pool_tc; + void *next_tc = tc_next_chunk(tc); + size_t old_chunk_size = TC_ALIGN16(TC_HDR_SIZE + tc->size); + size_t new_chunk_size = TC_ALIGN16(TC_HDR_SIZE + size); + size_t space_needed; + size_t space_left; + unsigned int chunk_count = pool_hdr->object_count; + + pool_tc = talloc_chunk_from_pool(pool_hdr); + if (!(pool_tc->flags & TALLOC_FLAG_FREE)) { + chunk_count -= 1; + } + + if (chunk_count == 1) { + /* + * optimize for the case where 'tc' is the only + * chunk in the pool. + */ + char *start = tc_pool_first_chunk(pool_hdr); + space_needed = new_chunk_size; + space_left = (char *)tc_pool_end(pool_hdr) - start; + + if (space_left >= space_needed) { + size_t old_used = TC_HDR_SIZE + tc->size; + size_t new_used = TC_HDR_SIZE + size; + new_ptr = start; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + { + /* + * The area from + * start -> tc may have + * been freed and thus been marked as + * VALGRIND_MEM_NOACCESS. Set it to + * VALGRIND_MEM_UNDEFINED so we can + * copy into it without valgrind errors. + * We can't just mark + * new_ptr -> new_ptr + old_used + * as this may overlap on top of tc, + * (which is why we use memmove, not + * memcpy below) hence the MIN. + */ + size_t undef_len = MIN((((char *)tc) - ((char *)new_ptr)),old_used); + VALGRIND_MAKE_MEM_UNDEFINED(new_ptr, undef_len); + } +#endif + + memmove(new_ptr, tc, old_used); + + tc = (struct talloc_chunk *)new_ptr; + TC_UNDEFINE_GROW_CHUNK(tc, size); + + /* + * first we do not align the pool pointer + * because we want to invalidate the padding + * too. + */ + pool_hdr->end = new_used + (char *)new_ptr; + tc_invalidate_pool(pool_hdr); + + /* now the aligned pointer */ + pool_hdr->end = new_chunk_size + (char *)new_ptr; + goto got_new_ptr; + } + + next_tc = NULL; + } + + if (new_chunk_size == old_chunk_size) { + TC_UNDEFINE_GROW_CHUNK(tc, size); + _talloc_chunk_set_not_free(tc); + tc->size = size; + return ptr; + } + + if (next_tc == pool_hdr->end) { + /* + * optimize for the case where 'tc' is the last + * chunk in the pool. + */ + space_needed = new_chunk_size - old_chunk_size; + space_left = tc_pool_space_left(pool_hdr); + + if (space_left >= space_needed) { + TC_UNDEFINE_GROW_CHUNK(tc, size); + _talloc_chunk_set_not_free(tc); + tc->size = size; + pool_hdr->end = tc_next_chunk(tc); + return ptr; + } + } + + new_ptr = tc_alloc_pool(tc, size + TC_HDR_SIZE, 0); + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + new_size = size; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + + _tc_free_poolmem(tc, __location__ "_talloc_realloc"); + } + } + else { + /* We're doing realloc here, so record the difference. */ + old_size = tc->size; + new_size = size; + new_ptr = realloc(tc, size + TC_HDR_SIZE); + } +got_new_ptr: +#endif + if (unlikely(!new_ptr)) { + /* + * Ok, this is a strange spot. We have to put back + * the old talloc_magic and any flags, except the + * TALLOC_FLAG_FREE as this was not free'ed by the + * realloc() call after all + */ + _talloc_chunk_set_not_free(tc); + return NULL; + } + + /* + * tc is now the new value from realloc(), the old memory we + * can't access any more and was preemptively marked as + * TALLOC_FLAG_FREE before the call. Now we mark it as not + * free again + */ + tc = (struct talloc_chunk *)new_ptr; + _talloc_chunk_set_not_free(tc); + if (malloced) { + tc->flags &= ~TALLOC_FLAG_POOLMEM; + } + if (tc->parent) { + tc->parent->child = tc; + } + if (tc->child) { + tc->child->parent = tc; + } + + if (tc->prev) { + tc->prev->next = tc; + } + if (tc->next) { + tc->next->prev = tc; + } + + if (new_size > old_size) { + talloc_memlimit_grow(tc->limit, new_size - old_size); + } else if (new_size < old_size) { + talloc_memlimit_shrink(tc->limit, old_size - new_size); + } + + tc->size = size; + _tc_set_name_const(tc, name); + + return TC_PTR_FROM_CHUNK(tc); +} + +/* + a wrapper around talloc_steal() for situations where you are moving a pointer + between two structures, and want the old pointer to be set to NULL +*/ +_PUBLIC_ void *_talloc_move(const void *new_ctx, const void *_pptr) +{ + const void **pptr = discard_const_p(const void *,_pptr); + void *ret = talloc_steal(new_ctx, discard_const_p(void, *pptr)); + (*pptr) = NULL; + return ret; +} + +enum talloc_mem_count_type { + TOTAL_MEM_SIZE, + TOTAL_MEM_BLOCKS, + TOTAL_MEM_LIMIT, +}; + +static inline size_t _talloc_total_mem_internal(const void *ptr, + enum talloc_mem_count_type type, + struct talloc_memlimit *old_limit, + struct talloc_memlimit *new_limit) +{ + size_t total = 0; + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(ptr); + + if (old_limit || new_limit) { + if (tc->limit && tc->limit->upper == old_limit) { + tc->limit->upper = new_limit; + } + } + + /* optimize in the memlimits case */ + if (type == TOTAL_MEM_LIMIT && + tc->limit != NULL && + tc->limit != old_limit && + tc->limit->parent == tc) { + return tc->limit->cur_size; + } + + if (tc->flags & TALLOC_FLAG_LOOP) { + return 0; + } + + tc->flags |= TALLOC_FLAG_LOOP; + + if (old_limit || new_limit) { + if (old_limit == tc->limit) { + tc->limit = new_limit; + } + } + + switch (type) { + case TOTAL_MEM_SIZE: + if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) { + total = tc->size; + } + break; + case TOTAL_MEM_BLOCKS: + total++; + break; + case TOTAL_MEM_LIMIT: + if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) { + /* + * Don't count memory allocated from a pool + * when calculating limits. Only count the + * pool itself. + */ + if (!(tc->flags & TALLOC_FLAG_POOLMEM)) { + if (tc->flags & TALLOC_FLAG_POOL) { + /* + * If this is a pool, the allocated + * size is in the pool header, and + * remember to add in the prefix + * length. + */ + struct talloc_pool_hdr *pool_hdr + = talloc_pool_from_chunk(tc); + total = pool_hdr->poolsize + + TC_HDR_SIZE + + TP_HDR_SIZE; + } else { + total = tc->size + TC_HDR_SIZE; + } + } + } + break; + } + for (c = tc->child; c; c = c->next) { + total += _talloc_total_mem_internal(TC_PTR_FROM_CHUNK(c), type, + old_limit, new_limit); + } + + tc->flags &= ~TALLOC_FLAG_LOOP; + + return total; +} + +/* + return the total size of a talloc pool (subtree) +*/ +_PUBLIC_ size_t talloc_total_size(const void *ptr) +{ + return _talloc_total_mem_internal(ptr, TOTAL_MEM_SIZE, NULL, NULL); +} + +/* + return the total number of blocks in a talloc pool (subtree) +*/ +_PUBLIC_ size_t talloc_total_blocks(const void *ptr) +{ + return _talloc_total_mem_internal(ptr, TOTAL_MEM_BLOCKS, NULL, NULL); +} + +/* + return the number of external references to a pointer +*/ +_PUBLIC_ size_t talloc_reference_count(const void *ptr) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); + struct talloc_reference_handle *h; + size_t ret = 0; + + for (h=tc->refs;h;h=h->next) { + ret++; + } + return ret; +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +_PUBLIC_ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data) +{ + struct talloc_chunk *c, *tc; + + if (ptr == NULL) { + ptr = null_context; + } + if (ptr == NULL) return; + + tc = talloc_chunk_from_ptr(ptr); + + if (tc->flags & TALLOC_FLAG_LOOP) { + return; + } + + callback(ptr, depth, max_depth, 0, private_data); + + if (max_depth >= 0 && depth >= max_depth) { + return; + } + + tc->flags |= TALLOC_FLAG_LOOP; + for (c=tc->child;c;c=c->next) { + if (c->name == TALLOC_MAGIC_REFERENCE) { + struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); + callback(h->ptr, depth + 1, max_depth, 1, private_data); + } else { + talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); + } + } + tc->flags &= ~TALLOC_FLAG_LOOP; +} + +static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) +{ + const char *name = __talloc_get_name(ptr); + struct talloc_chunk *tc; + FILE *f = (FILE *)_f; + + if (is_ref) { + fprintf(f, "%*sreference to: %s\n", depth*4, "", name); + return; + } + + tc = talloc_chunk_from_ptr(ptr); + if (tc->limit && tc->limit->parent == tc) { + fprintf(f, "%*s%-30s is a memlimit context" + " (max_size = %lu bytes, cur_size = %lu bytes)\n", + depth*4, "", + name, + (unsigned long)tc->limit->max_size, + (unsigned long)tc->limit->cur_size); + } + + if (depth == 0) { + fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", + (max_depth < 0 ? "full " :""), name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr)); + return; + } + + fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", + depth*4, "", + name, + (unsigned long)talloc_total_size(ptr), + (unsigned long)talloc_total_blocks(ptr), + (int)talloc_reference_count(ptr), ptr); + +#if 0 + fprintf(f, "content: "); + if (talloc_total_size(ptr)) { + int tot = talloc_total_size(ptr); + int i; + + for (i = 0; i < tot; i++) { + if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { + fprintf(f, "%c", ((char *)ptr)[i]); + } else { + fprintf(f, "~%02x", ((char *)ptr)[i]); + } + } + } + fprintf(f, "\n"); +#endif +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +_PUBLIC_ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) +{ + if (f) { + talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); + fflush(f); + } +} + +/* + report on memory usage by all children of a pointer, giving a full tree view +*/ +_PUBLIC_ void talloc_report_full(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, -1, f); +} + +/* + report on memory usage by all children of a pointer +*/ +_PUBLIC_ void talloc_report(const void *ptr, FILE *f) +{ + talloc_report_depth_file(ptr, 0, 1, f); +} + +/* + enable tracking of the NULL context +*/ +_PUBLIC_ void talloc_enable_null_tracking(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + if (autofree_context != NULL) { + talloc_reparent(NULL, null_context, autofree_context); + } + } +} + +/* + enable tracking of the NULL context, not moving the autofree context + into the NULL context. This is needed for the talloc testsuite +*/ +_PUBLIC_ void talloc_enable_null_tracking_no_autofree(void) +{ + if (null_context == NULL) { + null_context = _talloc_named_const(NULL, 0, "null_context"); + } +} + +/* + disable tracking of the NULL context +*/ +_PUBLIC_ void talloc_disable_null_tracking(void) +{ + if (null_context != NULL) { + /* we have to move any children onto the real NULL + context */ + struct talloc_chunk *tc, *tc2; + tc = talloc_chunk_from_ptr(null_context); + for (tc2 = tc->child; tc2; tc2=tc2->next) { + if (tc2->parent == tc) tc2->parent = NULL; + if (tc2->prev == tc) tc2->prev = NULL; + } + for (tc2 = tc->next; tc2; tc2=tc2->next) { + if (tc2->parent == tc) tc2->parent = NULL; + if (tc2->prev == tc) tc2->prev = NULL; + } + tc->child = NULL; + tc->next = NULL; + } + talloc_free(null_context); + null_context = NULL; +} + +/* + enable leak reporting on exit +*/ +_PUBLIC_ void talloc_enable_leak_report(void) +{ + talloc_enable_null_tracking(); + talloc_report_null = true; + talloc_setup_atexit(); +} + +/* + enable full leak reporting on exit +*/ +_PUBLIC_ void talloc_enable_leak_report_full(void) +{ + talloc_enable_null_tracking(); + talloc_report_null_full = true; + talloc_setup_atexit(); +} + +/* + talloc and zero memory. +*/ +_PUBLIC_ void *_talloc_zero(const void *ctx, size_t size, const char *name) +{ + void *p = _talloc_named_const(ctx, size, name); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + +/* + memdup with a talloc. +*/ +_PUBLIC_ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) +{ + void *newp = _talloc_named_const(t, size, name); + + if (likely(newp)) { + memcpy(newp, p, size); + } + + return newp; +} + +static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) +{ + char *ret; + struct talloc_chunk *tc; + + ret = (char *)__talloc(t, len + 1, &tc); + if (unlikely(!ret)) return NULL; + + memcpy(ret, p, len); + ret[len] = 0; + + _tc_set_name_const(tc, ret); + return ret; +} + +/* + strdup with a talloc +*/ +_PUBLIC_ char *talloc_strdup(const void *t, const char *p) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strlen(p)); +} + +/* + strndup with a talloc +*/ +_PUBLIC_ char *talloc_strndup(const void *t, const char *p, size_t n) +{ + if (unlikely(!p)) return NULL; + return __talloc_strlendup(t, p, strnlen(p, n)); +} + +static inline char *__talloc_strlendup_append(char *s, size_t slen, + const char *a, size_t alen) +{ + char *ret; + + ret = talloc_realloc(NULL, s, char, slen + alen + 1); + if (unlikely(!ret)) return NULL; + + /* append the string and the trailing \0 */ + memcpy(&ret[slen], a, alen); + ret[slen+alen] = 0; + + _tc_set_name_const(talloc_chunk_from_ptr(ret), ret); + return ret; +} + +/* + * Appends at the end of the string. + */ +_PUBLIC_ char *talloc_strdup_append(char *s, const char *a) +{ + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +_PUBLIC_ char *talloc_strdup_append_buffer(char *s, const char *a) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strdup(NULL, a); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strlen(a)); +} + +/* + * Appends at the end of the string. + */ +_PUBLIC_ char *talloc_strndup_append(char *s, const char *a, size_t n) +{ + if (unlikely(!s)) { + return talloc_strndup(NULL, a, n); + } + + if (unlikely(!a)) { + return s; + } + + return __talloc_strlendup_append(s, strlen(s), a, strnlen(a, n)); +} + +/* + * Appends at the end of the talloc'ed buffer, + * not the end of the string. + */ +_PUBLIC_ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_strndup(NULL, a, n); + } + + if (unlikely(!a)) { + return s; + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_strlendup_append(s, slen, a, strnlen(a, n)); +} + +static struct talloc_chunk *_vasprintf_tc(const void *t, + const char *fmt, + va_list ap) PRINTF_ATTRIBUTE(2,0); + +static struct talloc_chunk *_vasprintf_tc(const void *t, + const char *fmt, + va_list ap) +{ + int vlen; + size_t len; + char *ret; + va_list ap2; + struct talloc_chunk *tc; + char buf[1024]; + + /* this call looks strange, but it makes it work on older solaris boxes */ + va_copy(ap2, ap); + vlen = vsnprintf(buf, sizeof(buf), fmt, ap2); + va_end(ap2); + if (unlikely(vlen < 0)) { + return NULL; + } + len = vlen; + if (unlikely(len + 1 < len)) { + return NULL; + } + + ret = (char *)__talloc(t, len+1, &tc); + if (unlikely(!ret)) return NULL; + + if (len < sizeof(buf)) { + memcpy(ret, buf, len+1); + } else { + va_copy(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + va_end(ap2); + } + + _tc_set_name_const(tc, ret); + return tc; +} + +_PUBLIC_ char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) +{ + struct talloc_chunk *tc = _vasprintf_tc(t, fmt, ap); + if (tc == NULL) { + return NULL; + } + return TC_PTR_FROM_CHUNK(tc); +} + + +/* + Perform string formatting, and return a pointer to newly allocated + memory holding the result, inside a memory pool. + */ +_PUBLIC_ char *talloc_asprintf(const void *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) + PRINTF_ATTRIBUTE(3,0); + +static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, + const char *fmt, va_list ap) +{ + ssize_t alen; + va_list ap2; + char c; + + va_copy(ap2, ap); + alen = vsnprintf(&c, 1, fmt, ap2); + va_end(ap2); + + if (alen <= 0) { + /* Either the vsnprintf failed or the format resulted in + * no characters being formatted. In the former case, we + * ought to return NULL, in the latter we ought to return + * the original string. Most current callers of this + * function expect it to never return NULL. + */ + return s; + } + + s = talloc_realloc(NULL, s, char, slen + alen + 1); + if (!s) return NULL; + + va_copy(ap2, ap); + vsnprintf(s + slen, alen + 1, fmt, ap2); + va_end(ap2); + + _tc_set_name_const(talloc_chunk_from_ptr(s), s); + return s; +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. Appends at the end + * of the string. + **/ +_PUBLIC_ char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) +{ + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); +} + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Always appends at the + * end of the talloc'ed buffer, not the end of the string. + **/ +_PUBLIC_ char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) +{ + size_t slen; + + if (unlikely(!s)) { + return talloc_vasprintf(NULL, fmt, ap); + } + + slen = talloc_get_size(s); + if (likely(slen > 0)) { + slen--; + } + + return __talloc_vaslenprintf_append(s, slen, fmt, ap); +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a string buffer. + */ +_PUBLIC_ char *talloc_asprintf_append(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(s, fmt, ap); + va_end(ap); + return s; +} + +/* + Realloc @p s to append the formatted result of @p fmt and return @p + s, which may have moved. Good for gradually accumulating output + into a buffer. + */ +_PUBLIC_ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append_buffer(s, fmt, ap); + va_end(ap); + return s; +} + +/* + alloc an array, checking for integer overflow in the array size +*/ +_PUBLIC_ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_named_const(ctx, el_size * count, name); +} + +/* + alloc an zero array, checking for integer overflow in the array size +*/ +_PUBLIC_ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_zero(ctx, el_size * count, name); +} + +/* + realloc an array, checking for integer overflow in the array size +*/ +_PUBLIC_ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) +{ + if (count >= MAX_TALLOC_SIZE/el_size) { + return NULL; + } + return _talloc_realloc(ctx, ptr, el_size * count, name); +} + +/* + a function version of talloc_realloc(), so it can be passed as a function pointer + to libraries that want a realloc function (a realloc function encapsulates + all the basic capabilities of an allocation library, which is why this is useful) +*/ +_PUBLIC_ void *talloc_realloc_fn(const void *context, void *ptr, size_t size) +{ + return _talloc_realloc(context, ptr, size, NULL); +} + + +static int talloc_autofree_destructor(void *ptr) +{ + autofree_context = NULL; + return 0; +} + +/* + return a context which will be auto-freed on exit + this is useful for reducing the noise in leak reports +*/ +_PUBLIC_ void *talloc_autofree_context(void) +{ + if (autofree_context == NULL) { + autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); + talloc_set_destructor(autofree_context, talloc_autofree_destructor); + talloc_setup_atexit(); + } + return autofree_context; +} + +_PUBLIC_ size_t talloc_get_size(const void *context) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + + return tc->size; +} + +/* + find a parent of this context that has the given name, if any +*/ +_PUBLIC_ void *talloc_find_parent_byname(const void *context, const char *name) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return NULL; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (tc->name && strcmp(tc->name, name) == 0) { + return TC_PTR_FROM_CHUNK(tc); + } + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + return NULL; +} + +/* + show the parentage of a context +*/ +_PUBLIC_ void talloc_show_parents(const void *context, FILE *file) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + fprintf(file, "talloc no parents for NULL\n"); + return; + } + + tc = talloc_chunk_from_ptr(context); + fprintf(file, "talloc parents of '%s'\n", __talloc_get_name(context)); + while (tc) { + fprintf(file, "\t'%s'\n", __talloc_get_name(TC_PTR_FROM_CHUNK(tc))); + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + } + } + fflush(file); +} + +/* + return 1 if ptr is a parent of context +*/ +static int _talloc_is_parent(const void *context, const void *ptr, int depth) +{ + struct talloc_chunk *tc; + + if (context == NULL) { + return 0; + } + + tc = talloc_chunk_from_ptr(context); + while (tc) { + if (depth <= 0) { + return 0; + } + if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; + while (tc && tc->prev) tc = tc->prev; + if (tc) { + tc = tc->parent; + depth--; + } + } + return 0; +} + +/* + return 1 if ptr is a parent of context +*/ +_PUBLIC_ int talloc_is_parent(const void *context, const void *ptr) +{ + return _talloc_is_parent(context, ptr, TALLOC_MAX_DEPTH); +} + +/* + return the total size of memory used by this context and all children +*/ +static inline size_t _talloc_total_limit_size(const void *ptr, + struct talloc_memlimit *old_limit, + struct talloc_memlimit *new_limit) +{ + return _talloc_total_mem_internal(ptr, TOTAL_MEM_LIMIT, + old_limit, new_limit); +} + +static inline bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size) +{ + struct talloc_memlimit *l; + + for (l = limit; l != NULL; l = l->upper) { + if (l->max_size != 0 && + ((l->max_size <= l->cur_size) || + (l->max_size - l->cur_size < size))) { + return false; + } + } + + return true; +} + +/* + Update memory limits when freeing a talloc_chunk. +*/ +static void tc_memlimit_update_on_free(struct talloc_chunk *tc) +{ + size_t limit_shrink_size; + + if (!tc->limit) { + return; + } + + /* + * Pool entries don't count. Only the pools + * themselves are counted as part of the memory + * limits. Note that this also takes care of + * nested pools which have both flags + * TALLOC_FLAG_POOLMEM|TALLOC_FLAG_POOL set. + */ + if (tc->flags & TALLOC_FLAG_POOLMEM) { + return; + } + + /* + * If we are part of a memory limited context hierarchy + * we need to subtract the memory used from the counters + */ + + limit_shrink_size = tc->size+TC_HDR_SIZE; + + /* + * If we're deallocating a pool, take into + * account the prefix size added for the pool. + */ + + if (tc->flags & TALLOC_FLAG_POOL) { + limit_shrink_size += TP_HDR_SIZE; + } + + talloc_memlimit_shrink(tc->limit, limit_shrink_size); + + if (tc->limit->parent == tc) { + free(tc->limit); + } + + tc->limit = NULL; +} + +/* + Increase memory limit accounting after a malloc/realloc. +*/ +static void talloc_memlimit_grow(struct talloc_memlimit *limit, + size_t size) +{ + struct talloc_memlimit *l; + + for (l = limit; l != NULL; l = l->upper) { + size_t new_cur_size = l->cur_size + size; + if (new_cur_size < l->cur_size) { + talloc_abort("logic error in talloc_memlimit_grow\n"); + return; + } + l->cur_size = new_cur_size; + } +} + +/* + Decrease memory limit accounting after a free/realloc. +*/ +static void talloc_memlimit_shrink(struct talloc_memlimit *limit, + size_t size) +{ + struct talloc_memlimit *l; + + for (l = limit; l != NULL; l = l->upper) { + if (l->cur_size < size) { + talloc_abort("logic error in talloc_memlimit_shrink\n"); + return; + } + l->cur_size = l->cur_size - size; + } +} + +_PUBLIC_ int talloc_set_memlimit(const void *ctx, size_t max_size) +{ + struct talloc_chunk *tc = talloc_chunk_from_ptr(ctx); + struct talloc_memlimit *orig_limit; + struct talloc_memlimit *limit = NULL; + + if (tc->limit && tc->limit->parent == tc) { + tc->limit->max_size = max_size; + return 0; + } + orig_limit = tc->limit; + + limit = malloc(sizeof(struct talloc_memlimit)); + if (limit == NULL) { + return 1; + } + limit->parent = tc; + limit->max_size = max_size; + limit->cur_size = _talloc_total_limit_size(ctx, tc->limit, limit); + + if (orig_limit) { + limit->upper = orig_limit; + } else { + limit->upper = NULL; + } + + return 0; +} diff --git a/app/src/main/cpp/proot/talloc/talloc.h b/app/src/main/cpp/proot/talloc/talloc.h new file mode 100644 index 0000000..763098e --- /dev/null +++ b/app/src/main/cpp/proot/talloc/talloc.h @@ -0,0 +1,1938 @@ +#ifndef _TALLOC_H_ +#define _TALLOC_H_ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + + Copyright (C) Andrew Tridgell 2004-2005 + Copyright (C) Stefan Metzmacher 2006 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup talloc The talloc API + * + * talloc is a hierarchical, reference counted memory pool system with + * destructors. It is the core memory allocator used in Samba. + * + * @{ + */ + +#define TALLOC_VERSION_MAJOR 2 +#define TALLOC_VERSION_MINOR 1 + +int talloc_version_major(void); +int talloc_version_minor(void); +/* This is mostly useful only for testing */ +int talloc_test_get_magic(void); + +/** + * @brief Define a talloc parent type + * + * As talloc is a hierarchial memory allocator, every talloc chunk is a + * potential parent to other talloc chunks. So defining a separate type for a + * talloc chunk is not strictly necessary. TALLOC_CTX is defined nevertheless, + * as it provides an indicator for function arguments. You will frequently + * write code like + * + * @code + * struct foo *foo_create(TALLOC_CTX *mem_ctx) + * { + * struct foo *result; + * result = talloc(mem_ctx, struct foo); + * if (result == NULL) return NULL; + * ... initialize foo ... + * return result; + * } + * @endcode + * + * In this type of allocating functions it is handy to have a general + * TALLOC_CTX type to indicate which parent to put allocated structures on. + */ +typedef void TALLOC_CTX; + +/* + this uses a little trick to allow __LINE__ to be stringified +*/ +#ifndef __location__ +#define __TALLOC_STRING_LINE1__(s) #s +#define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) +#define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ +#endif + +#ifndef TALLOC_DEPRECATED +#define TALLOC_DEPRECATED 0 +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +#ifdef DOXYGEN +/** + * @brief Create a new talloc context. + * + * The talloc() macro is the core of the talloc library. It takes a memory + * context and a type, and returns a pointer to a new area of memory of the + * given type. + * + * The returned pointer is itself a talloc context, so you can use it as the + * context argument to more calls to talloc if you wish. + * + * The returned pointer is a "child" of the supplied context. This means that if + * you talloc_free() the context then the new child disappears as well. + * Alternatively you can free just the child. + * + * @param[in] ctx A talloc context to create a new reference on or NULL to + * create a new top level context. + * + * @param[in] type The type of memory to allocate. + * + * @return A type casted talloc context or NULL on error. + * + * @code + * unsigned int *a, *b; + * + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * @endcode + * + * @see talloc_zero + * @see talloc_array + * @see talloc_steal + * @see talloc_free + */ +void *talloc(const void *ctx, #type); +#else +#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) +void *_talloc(const void *context, size_t size); +#endif + +/** + * @brief Create a new top level talloc context. + * + * This function creates a zero length named talloc context as a top level + * context. It is equivalent to: + * + * @code + * talloc_named(NULL, 0, fmt, ...); + * @endcode + * @param[in] fmt Format string for the name. + * + * @param[in] ... Additional printf-style arguments. + * + * @return The allocated memory chunk, NULL on error. + * + * @see talloc_named() + */ +void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); + +#ifdef DOXYGEN +/** + * @brief Free a chunk of talloc memory. + * + * The talloc_free() function frees a piece of talloc memory, and all its + * children. You can call talloc_free() on any pointer returned by + * talloc(). + * + * The return value of talloc_free() indicates success or failure, with 0 + * returned for success and -1 for failure. A possible failure condition + * is if the pointer had a destructor attached to it and the destructor + * returned -1. See talloc_set_destructor() for details on + * destructors. Likewise, if "ptr" is NULL, then the function will make + * no modifications and return -1. + * + * From version 2.0 and onwards, as a special case, talloc_free() is + * refused on pointers that have more than one parent associated, as talloc + * would have no way of knowing which parent should be removed. This is + * different from older versions in the sense that always the reference to + * the most recently established parent has been destroyed. Hence to free a + * pointer that has more than one parent please use talloc_unlink(). + * + * To help you find problems in your code caused by this behaviour, if + * you do try and free a pointer with more than one parent then the + * talloc logging function will be called to give output like this: + * + * @code + * ERROR: talloc_free with references at some_dir/source/foo.c:123 + * reference at some_dir/source/other.c:325 + * reference at some_dir/source/third.c:121 + * @endcode + * + * Please see the documentation for talloc_set_log_fn() and + * talloc_set_log_stderr() for more information on talloc logging + * functions. + * + * If TALLOC_FREE_FILL environment variable is set, + * the memory occupied by the context is filled with the value of this variable. + * The value should be a numeric representation of the character you want to + * use. + * + * talloc_free() operates recursively on its children. + * + * @param[in] ptr The chunk to be freed. + * + * @return Returns 0 on success and -1 on error. A possible + * failure condition is if the pointer had a destructor + * attached to it and the destructor returned -1. Likewise, + * if "ptr" is NULL, then the function will make no + * modifications and returns -1. + * + * Example: + * @code + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * + * talloc_free(a); // Frees a and b + * @endcode + * + * @see talloc_set_destructor() + * @see talloc_unlink() + */ +int talloc_free(void *ptr); +#else +#define talloc_free(ctx) _talloc_free(ctx, __location__) +int _talloc_free(void *ptr, const char *location); +#endif + +/** + * @brief Free a talloc chunk's children. + * + * The function walks along the list of all children of a talloc context and + * talloc_free()s only the children, not the context itself. + * + * A NULL argument is handled as no-op. + * + * @param[in] ptr The chunk that you want to free the children of + * (NULL is allowed too) + */ +void talloc_free_children(void *ptr); + +#ifdef DOXYGEN +/** + * @brief Assign a destructor function to be called when a chunk is freed. + * + * The function talloc_set_destructor() sets the "destructor" for the pointer + * "ptr". A destructor is a function that is called when the memory used by a + * pointer is about to be released. The destructor receives the pointer as an + * argument, and should return 0 for success and -1 for failure. + * + * The destructor can do anything it wants to, including freeing other pieces + * of memory. A common use for destructors is to clean up operating system + * resources (such as open file descriptors) contained in the structure the + * destructor is placed on. + * + * You can only place one destructor on a pointer. If you need more than one + * destructor then you can create a zero-length child of the pointer and place + * an additional destructor on that. + * + * To remove a destructor call talloc_set_destructor() with NULL for the + * destructor. + * + * If your destructor attempts to talloc_free() the pointer that it is the + * destructor for then talloc_free() will return -1 and the free will be + * ignored. This would be a pointless operation anyway, as the destructor is + * only called when the memory is just about to go away. + * + * @param[in] ptr The talloc chunk to add a destructor to. + * + * @param[in] destructor The destructor function to be called. NULL to remove + * it. + * + * Example: + * @code + * static int destroy_fd(int *fd) { + * close(*fd); + * return 0; + * } + * + * int *open_file(const char *filename) { + * int *fd = talloc(NULL, int); + * *fd = open(filename, O_RDONLY); + * if (*fd < 0) { + * talloc_free(fd); + * return NULL; + * } + * // Whenever they free this, we close the file. + * talloc_set_destructor(fd, destroy_fd); + * return fd; + * } + * @endcode + * + * @see talloc() + * @see talloc_free() + */ +void talloc_set_destructor(const void *ptr, int (*destructor)(void *)); + +/** + * @brief Change a talloc chunk's parent. + * + * The talloc_steal() function changes the parent context of a talloc + * pointer. It is typically used when the context that the pointer is + * currently a child of is going to be freed and you wish to keep the + * memory for a longer time. + * + * To make the changed hierarchy less error-prone, you might consider to use + * talloc_move(). + * + * If you try and call talloc_steal() on a pointer that has more than one + * parent then the result is ambiguous. Talloc will choose to remove the + * parent that is currently indicated by talloc_parent() and replace it with + * the chosen parent. You will also get a message like this via the talloc + * logging functions: + * + * @code + * WARNING: talloc_steal with references at some_dir/source/foo.c:123 + * reference at some_dir/source/other.c:325 + * reference at some_dir/source/third.c:121 + * @endcode + * + * To unambiguously change the parent of a pointer please see the function + * talloc_reparent(). See the talloc_set_log_fn() documentation for more + * information on talloc logging. + * + * @param[in] new_ctx The new parent context. + * + * @param[in] ptr The talloc chunk to move. + * + * @return Returns the pointer that you pass it. It does not have + * any failure modes. + * + * @note It is possible to produce loops in the parent/child relationship + * if you are not careful with talloc_steal(). No guarantees are provided + * as to your sanity or the safety of your data if you do this. + */ +void *talloc_steal(const void *new_ctx, const void *ptr); +#else /* DOXYGEN */ +/* try to make talloc_set_destructor() and talloc_steal() type safe, + if we have a recent gcc */ +#if (__GNUC__ >= 3) +#define _TALLOC_TYPEOF(ptr) __typeof__(ptr) +#define talloc_set_destructor(ptr, function) \ + do { \ + int (*_talloc_destructor_fn)(_TALLOC_TYPEOF(ptr)) = (function); \ + _talloc_set_destructor((ptr), (int (*)(void *))_talloc_destructor_fn); \ + } while(0) +/* this extremely strange macro is to avoid some braindamaged warning + stupidity in gcc 4.1.x */ +#define talloc_steal(ctx, ptr) ({ _TALLOC_TYPEOF(ptr) __talloc_steal_ret = (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__); __talloc_steal_ret; }) +#else /* __GNUC__ >= 3 */ +#define talloc_set_destructor(ptr, function) \ + _talloc_set_destructor((ptr), (int (*)(void *))(function)) +#define _TALLOC_TYPEOF(ptr) void * +#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal_loc((ctx),(ptr), __location__) +#endif /* __GNUC__ >= 3 */ +void _talloc_set_destructor(const void *ptr, int (*_destructor)(void *)); +void *_talloc_steal_loc(const void *new_ctx, const void *ptr, const char *location); +#endif /* DOXYGEN */ + +/** + * @brief Assign a name to a talloc chunk. + * + * Each talloc pointer has a "name". The name is used principally for + * debugging purposes, although it is also possible to set and get the name on + * a pointer in as a way of "marking" pointers in your code. + * + * The main use for names on pointer is for "talloc reports". See + * talloc_report() and talloc_report_full() for details. Also see + * talloc_enable_leak_report() and talloc_enable_leak_report_full(). + * + * The talloc_set_name() function allocates memory as a child of the + * pointer. It is logically equivalent to: + * + * @code + * talloc_set_name_const(ptr, talloc_asprintf(ptr, fmt, ...)); + * @endcode + * + * @param[in] ptr The talloc chunk to assign a name to. + * + * @param[in] fmt Format string for the name. + * + * @param[in] ... Add printf-style additional arguments. + * + * @return The assigned name, NULL on error. + * + * @note Multiple calls to talloc_set_name() will allocate more memory without + * releasing the name. All of the memory is released when the ptr is freed + * using talloc_free(). + */ +const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +#ifdef DOXYGEN +/** + * @brief Change a talloc chunk's parent. + * + * This function has the same effect as talloc_steal(), and additionally sets + * the source pointer to NULL. You would use it like this: + * + * @code + * struct foo *X = talloc(tmp_ctx, struct foo); + * struct foo *Y; + * Y = talloc_move(new_ctx, &X); + * @endcode + * + * @param[in] new_ctx The new parent context. + * + * @param[in] pptr Pointer to a pointer to the talloc chunk to move. + * + * @return The pointer to the talloc chunk that moved. + * It does not have any failure modes. + * + */ +void *talloc_move(const void *new_ctx, void **pptr); +#else +#define talloc_move(ctx, pptr) (_TALLOC_TYPEOF(*(pptr)))_talloc_move((ctx),(void *)(pptr)) +void *_talloc_move(const void *new_ctx, const void *pptr); +#endif + +/** + * @brief Assign a name to a talloc chunk. + * + * The function is just like talloc_set_name(), but it takes a string constant, + * and is much faster. It is extensively used by the "auto naming" macros, such + * as talloc_p(). + * + * This function does not allocate any memory. It just copies the supplied + * pointer into the internal representation of the talloc ptr. This means you + * must not pass a name pointer to memory that will disappear before the ptr + * is freed with talloc_free(). + * + * @param[in] ptr The talloc chunk to assign a name to. + * + * @param[in] name Format string for the name. + */ +void talloc_set_name_const(const void *ptr, const char *name); + +/** + * @brief Create a named talloc chunk. + * + * The talloc_named() function creates a named talloc pointer. It is + * equivalent to: + * + * @code + * ptr = talloc_size(context, size); + * talloc_set_name(ptr, fmt, ....); + * @endcode + * + * @param[in] context The talloc context to hang the result off. + * + * @param[in] size Number of char's that you want to allocate. + * + * @param[in] fmt Format string for the name. + * + * @param[in] ... Additional printf-style arguments. + * + * @return The allocated memory chunk, NULL on error. + * + * @see talloc_set_name() + */ +void *talloc_named(const void *context, size_t size, + const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); + +/** + * @brief Basic routine to allocate a chunk of memory. + * + * This is equivalent to: + * + * @code + * ptr = talloc_size(context, size); + * talloc_set_name_const(ptr, name); + * @endcode + * + * @param[in] context The parent context. + * + * @param[in] size The number of char's that we want to allocate. + * + * @param[in] name The name the talloc block has. + * + * @return The allocated memory chunk, NULL on error. + */ +void *talloc_named_const(const void *context, size_t size, const char *name); + +#ifdef DOXYGEN +/** + * @brief Untyped allocation. + * + * The function should be used when you don't have a convenient type to pass to + * talloc(). Unlike talloc(), it is not type safe (as it returns a void *), so + * you are on your own for type checking. + * + * Best to use talloc() or talloc_array() instead. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] size Number of char's that you want to allocate. + * + * @return The allocated memory chunk, NULL on error. + * + * Example: + * @code + * void *mem = talloc_size(NULL, 100); + * @endcode + */ +void *talloc_size(const void *ctx, size_t size); +#else +#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Allocate into a typed pointer. + * + * The talloc_ptrtype() macro should be used when you have a pointer and want + * to allocate memory to point at with this pointer. When compiling with + * gcc >= 3 it is typesafe. Note this is a wrapper of talloc_size() and + * talloc_get_name() will return the current location in the source file and + * not the type. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] type The pointer you want to assign the result to. + * + * @return The properly casted allocated memory chunk, NULL on + * error. + * + * Example: + * @code + * unsigned int *a = talloc_ptrtype(NULL, a); + * @endcode + */ +void *talloc_ptrtype(const void *ctx, #type); +#else +#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) +#endif + +#ifdef DOXYGEN +/** + * @brief Allocate a new 0-sized talloc chunk. + * + * This is a utility macro that creates a new memory context hanging off an + * existing context, automatically naming it "talloc_new: __location__" where + * __location__ is the source line it is called from. It is particularly + * useful for creating a new temporary working context. + * + * @param[in] ctx The talloc parent context. + * + * @return A new talloc chunk, NULL on error. + */ +void *talloc_new(const void *ctx); +#else +#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Allocate a 0-initizialized structure. + * + * The macro is equivalent to: + * + * @code + * ptr = talloc(ctx, type); + * if (ptr) memset(ptr, 0, sizeof(type)); + * @endcode + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] type The type that we want to allocate. + * + * @return Pointer to a piece of memory, properly cast to 'type *', + * NULL on error. + * + * Example: + * @code + * unsigned int *a, *b; + * a = talloc_zero(NULL, unsigned int); + * b = talloc_zero(a, unsigned int); + * @endcode + * + * @see talloc() + * @see talloc_zero_size() + * @see talloc_zero_array() + */ +void *talloc_zero(const void *ctx, #type); + +/** + * @brief Allocate untyped, 0-initialized memory. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] size Number of char's that you want to allocate. + * + * @return The allocated memory chunk. + */ +void *talloc_zero_size(const void *ctx, size_t size); +#else +#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) +#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) +void *_talloc_zero(const void *ctx, size_t size, const char *name); +#endif + +/** + * @brief Return the name of a talloc chunk. + * + * @param[in] ptr The talloc chunk. + * + * @return The current name for the given talloc pointer. + * + * @see talloc_set_name() + */ +const char *talloc_get_name(const void *ptr); + +/** + * @brief Verify that a talloc chunk carries a specified name. + * + * This function checks if a pointer has the specified name. If it does + * then the pointer is returned. + * + * @param[in] ptr The talloc chunk to check. + * + * @param[in] name The name to check against. + * + * @return The pointer if the name matches, NULL if it doesn't. + */ +void *talloc_check_name(const void *ptr, const char *name); + +/** + * @brief Get the parent chunk of a pointer. + * + * @param[in] ptr The talloc pointer to inspect. + * + * @return The talloc parent of ptr, NULL on error. + */ +void *talloc_parent(const void *ptr); + +/** + * @brief Get a talloc chunk's parent name. + * + * @param[in] ptr The talloc pointer to inspect. + * + * @return The name of ptr's parent chunk. + */ +const char *talloc_parent_name(const void *ptr); + +/** + * @brief Get the total size of a talloc chunk including its children. + * + * The function returns the total size in bytes used by this pointer and all + * child pointers. Mostly useful for debugging. + * + * Passing NULL is allowed, but it will only give a meaningful result if + * talloc_enable_leak_report() or talloc_enable_leak_report_full() has + * been called. + * + * @param[in] ptr The talloc chunk. + * + * @return The total size. + */ +size_t talloc_total_size(const void *ptr); + +/** + * @brief Get the number of talloc chunks hanging off a chunk. + * + * The talloc_total_blocks() function returns the total memory block + * count used by this pointer and all child pointers. Mostly useful for + * debugging. + * + * Passing NULL is allowed, but it will only give a meaningful result if + * talloc_enable_leak_report() or talloc_enable_leak_report_full() has + * been called. + * + * @param[in] ptr The talloc chunk. + * + * @return The total size. + */ +size_t talloc_total_blocks(const void *ptr); + +#ifdef DOXYGEN +/** + * @brief Duplicate a memory area into a talloc chunk. + * + * The function is equivalent to: + * + * @code + * ptr = talloc_size(ctx, size); + * if (ptr) memcpy(ptr, p, size); + * @endcode + * + * @param[in] t The talloc context to hang the result off. + * + * @param[in] p The memory chunk you want to duplicate. + * + * @param[in] size Number of char's that you want copy. + * + * @return The allocated memory chunk. + * + * @see talloc_size() + */ +void *talloc_memdup(const void *t, const void *p, size_t size); +#else +#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) +void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); +#endif + +#ifdef DOXYGEN +/** + * @brief Assign a type to a talloc chunk. + * + * This macro allows you to force the name of a pointer to be of a particular + * type. This can be used in conjunction with talloc_get_type() to do type + * checking on void* pointers. + * + * It is equivalent to this: + * + * @code + * talloc_set_name_const(ptr, #type) + * @endcode + * + * @param[in] ptr The talloc chunk to assign the type to. + * + * @param[in] type The type to assign. + */ +void talloc_set_type(const char *ptr, #type); + +/** + * @brief Get a typed pointer out of a talloc pointer. + * + * This macro allows you to do type checking on talloc pointers. It is + * particularly useful for void* private pointers. It is equivalent to + * this: + * + * @code + * (type *)talloc_check_name(ptr, #type) + * @endcode + * + * @param[in] ptr The talloc pointer to check. + * + * @param[in] type The type to check against. + * + * @return The properly casted pointer given by ptr, NULL on error. + */ +type *talloc_get_type(const void *ptr, #type); +#else +#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) +#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) +#endif + +#ifdef DOXYGEN +/** + * @brief Safely turn a void pointer into a typed pointer. + * + * This macro is used together with talloc(mem_ctx, struct foo). If you had to + * assign the talloc chunk pointer to some void pointer variable, + * talloc_get_type_abort() is the recommended way to get the convert the void + * pointer back to a typed pointer. + * + * @param[in] ptr The void pointer to convert. + * + * @param[in] type The type that this chunk contains + * + * @return The same value as ptr, type-checked and properly cast. + */ +void *talloc_get_type_abort(const void *ptr, #type); +#else +#ifdef TALLOC_GET_TYPE_ABORT_NOOP +#define talloc_get_type_abort(ptr, type) (type *)(ptr) +#else +#define talloc_get_type_abort(ptr, type) (type *)_talloc_get_type_abort(ptr, #type, __location__) +#endif +void *_talloc_get_type_abort(const void *ptr, const char *name, const char *location); +#endif + +/** + * @brief Find a parent context by name. + * + * Find a parent memory context of the current context that has the given + * name. This can be very useful in complex programs where it may be + * difficult to pass all information down to the level you need, but you + * know the structure you want is a parent of another context. + * + * @param[in] ctx The talloc chunk to start from. + * + * @param[in] name The name of the parent we look for. + * + * @return The memory context we are looking for, NULL if not + * found. + */ +void *talloc_find_parent_byname(const void *ctx, const char *name); + +#ifdef DOXYGEN +/** + * @brief Find a parent context by type. + * + * Find a parent memory context of the current context that has the given + * name. This can be very useful in complex programs where it may be + * difficult to pass all information down to the level you need, but you + * know the structure you want is a parent of another context. + * + * Like talloc_find_parent_byname() but takes a type, making it typesafe. + * + * @param[in] ptr The talloc chunk to start from. + * + * @param[in] type The type of the parent to look for. + * + * @return The memory context we are looking for, NULL if not + * found. + */ +void *talloc_find_parent_bytype(const void *ptr, #type); +#else +#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) +#endif + +/** + * @brief Allocate a talloc pool. + * + * A talloc pool is a pure optimization for specific situations. In the + * release process for Samba 3.2 we found out that we had become considerably + * slower than Samba 3.0 was. Profiling showed that malloc(3) was a large CPU + * consumer in benchmarks. For Samba 3.2 we have internally converted many + * static buffers to dynamically allocated ones, so malloc(3) being beaten + * more was no surprise. But it made us slower. + * + * talloc_pool() is an optimization to call malloc(3) a lot less for the use + * pattern Samba has: The SMB protocol is mainly a request/response protocol + * where we have to allocate a certain amount of memory per request and free + * that after the SMB reply is sent to the client. + * + * talloc_pool() creates a talloc chunk that you can use as a talloc parent + * exactly as you would use any other ::TALLOC_CTX. The difference is that + * when you talloc a child of this pool, no malloc(3) is done. Instead, talloc + * just increments a pointer inside the talloc_pool. This also works + * recursively. If you use the child of the talloc pool as a parent for + * grand-children, their memory is also taken from the talloc pool. + * + * If there is not enough memory in the pool to allocate the new child, + * it will create a new talloc chunk as if the parent was a normal talloc + * context. + * + * If you talloc_free() children of a talloc pool, the memory is not given + * back to the system. Instead, free(3) is only called if the talloc_pool() + * itself is released with talloc_free(). + * + * The downside of a talloc pool is that if you talloc_move() a child of a + * talloc pool to a talloc parent outside the pool, the whole pool memory is + * not free(3)'ed until that moved chunk is also talloc_free()ed. + * + * @param[in] context The talloc context to hang the result off. + * + * @param[in] size Size of the talloc pool. + * + * @return The allocated talloc pool, NULL on error. + */ +void *talloc_pool(const void *context, size_t size); + +#ifdef DOXYGEN +/** + * @brief Allocate a talloc object as/with an additional pool. + * + * This is like talloc_pool(), but's it's more flexible + * and allows an object to be a pool for its children. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] type The type that we want to allocate. + * + * @param[in] num_subobjects The expected number of subobjects, which will + * be allocated within the pool. This allocates + * space for talloc_chunk headers. + * + * @param[in] total_subobjects_size The size that all subobjects can use in total. + * + * + * @return The allocated talloc object, NULL on error. + */ +void *talloc_pooled_object(const void *ctx, #type, + unsigned num_subobjects, + size_t total_subobjects_size); +#else +#define talloc_pooled_object(_ctx, _type, \ + _num_subobjects, \ + _total_subobjects_size) \ + (_type *)_talloc_pooled_object((_ctx), sizeof(_type), #_type, \ + (_num_subobjects), \ + (_total_subobjects_size)) +void *_talloc_pooled_object(const void *ctx, + size_t type_size, + const char *type_name, + unsigned num_subobjects, + size_t total_subobjects_size); +#endif + +/** + * @brief Free a talloc chunk and NULL out the pointer. + * + * TALLOC_FREE() frees a pointer and sets it to NULL. Use this if you want + * immediate feedback (i.e. crash) if you use a pointer after having free'ed + * it. + * + * @param[in] ctx The chunk to be freed. + */ +#define TALLOC_FREE(ctx) do { if (ctx != NULL) { talloc_free(ctx); ctx=NULL; } } while(0) + +/* @} ******************************************************************/ + +/** + * \defgroup talloc_ref The talloc reference function. + * @ingroup talloc + * + * This module contains the definitions around talloc references + * + * @{ + */ + +/** + * @brief Increase the reference count of a talloc chunk. + * + * The talloc_increase_ref_count(ptr) function is exactly equivalent to: + * + * @code + * talloc_reference(NULL, ptr); + * @endcode + * + * You can use either syntax, depending on which you think is clearer in + * your code. + * + * @param[in] ptr The pointer to increase the reference count. + * + * @return 0 on success, -1 on error. + */ +int talloc_increase_ref_count(const void *ptr); + +/** + * @brief Get the number of references to a talloc chunk. + * + * @param[in] ptr The pointer to retrieve the reference count from. + * + * @return The number of references. + */ +size_t talloc_reference_count(const void *ptr); + +#ifdef DOXYGEN +/** + * @brief Create an additional talloc parent to a pointer. + * + * The talloc_reference() function makes "context" an additional parent of + * ptr. Each additional reference consumes around 48 bytes of memory on intel + * x86 platforms. + * + * If ptr is NULL, then the function is a no-op, and simply returns NULL. + * + * After creating a reference you can free it in one of the following ways: + * + * - you can talloc_free() any parent of the original pointer. That + * will reduce the number of parents of this pointer by 1, and will + * cause this pointer to be freed if it runs out of parents. + * + * - you can talloc_free() the pointer itself if it has at maximum one + * parent. This behaviour has been changed since the release of version + * 2.0. Further informations in the description of "talloc_free". + * + * For more control on which parent to remove, see talloc_unlink() + * @param[in] ctx The additional parent. + * + * @param[in] ptr The pointer you want to create an additional parent for. + * + * @return The original pointer 'ptr', NULL if talloc ran out of + * memory in creating the reference. + * + * @warning You should try to avoid using this interface. It turns a beautiful + * talloc-tree into a graph. It is often really hard to debug if you + * screw something up by accident. + * + * Example: + * @code + * unsigned int *a, *b, *c; + * a = talloc(NULL, unsigned int); + * b = talloc(NULL, unsigned int); + * c = talloc(a, unsigned int); + * // b also serves as a parent of c. + * talloc_reference(b, c); + * @endcode + * + * @see talloc_unlink() + */ +void *talloc_reference(const void *ctx, const void *ptr); +#else +#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference_loc((ctx),(ptr), __location__) +void *_talloc_reference_loc(const void *context, const void *ptr, const char *location); +#endif + +/** + * @brief Remove a specific parent from a talloc chunk. + * + * The function removes a specific parent from ptr. The context passed must + * either be a context used in talloc_reference() with this pointer, or must be + * a direct parent of ptr. + * + * You can just use talloc_free() instead of talloc_unlink() if there + * is at maximum one parent. This behaviour has been changed since the + * release of version 2.0. Further informations in the description of + * "talloc_free". + * + * @param[in] context The talloc parent to remove. + * + * @param[in] ptr The talloc ptr you want to remove the parent from. + * + * @return 0 on success, -1 on error. + * + * @note If the parent has already been removed using talloc_free() then + * this function will fail and will return -1. Likewise, if ptr is NULL, + * then the function will make no modifications and return -1. + * + * @warning You should try to avoid using this interface. It turns a beautiful + * talloc-tree into a graph. It is often really hard to debug if you + * screw something up by accident. + * + * Example: + * @code + * unsigned int *a, *b, *c; + * a = talloc(NULL, unsigned int); + * b = talloc(NULL, unsigned int); + * c = talloc(a, unsigned int); + * // b also serves as a parent of c. + * talloc_reference(b, c); + * talloc_unlink(b, c); + * @endcode + */ +int talloc_unlink(const void *context, void *ptr); + +/** + * @brief Provide a talloc context that is freed at program exit. + * + * This is a handy utility function that returns a talloc context + * which will be automatically freed on program exit. This can be used + * to reduce the noise in memory leak reports. + * + * Never use this in code that might be used in objects loaded with + * dlopen and unloaded with dlclose. talloc_autofree_context() + * internally uses atexit(3). Some platforms like modern Linux handles + * this fine, but for example FreeBSD does not deal well with dlopen() + * and atexit() used simultaneously: dlclose() does not clean up the + * list of atexit-handlers, so when the program exits the code that + * was registered from within talloc_autofree_context() is gone, the + * program crashes at exit. + * + * @return A talloc context, NULL on error. + */ +void *talloc_autofree_context(void); + +/** + * @brief Get the size of a talloc chunk. + * + * This function lets you know the amount of memory allocated so far by + * this context. It does NOT account for subcontext memory. + * This can be used to calculate the size of an array. + * + * @param[in] ctx The talloc chunk. + * + * @return The size of the talloc chunk. + */ +size_t talloc_get_size(const void *ctx); + +/** + * @brief Show the parentage of a context. + * + * @param[in] context The talloc context to look at. + * + * @param[in] file The output to use, a file, stdout or stderr. + */ +void talloc_show_parents(const void *context, FILE *file); + +/** + * @brief Check if a context is parent of a talloc chunk. + * + * This checks if context is referenced in the talloc hierarchy above ptr. + * + * @param[in] context The assumed talloc context. + * + * @param[in] ptr The talloc chunk to check. + * + * @return Return 1 if this is the case, 0 if not. + */ +int talloc_is_parent(const void *context, const void *ptr); + +/** + * @brief Change the parent context of a talloc pointer. + * + * The function changes the parent context of a talloc pointer. It is typically + * used when the context that the pointer is currently a child of is going to be + * freed and you wish to keep the memory for a longer time. + * + * The difference between talloc_reparent() and talloc_steal() is that + * talloc_reparent() can specify which parent you wish to change. This is + * useful when a pointer has multiple parents via references. + * + * @param[in] old_parent + * @param[in] new_parent + * @param[in] ptr + * + * @return Return the pointer you passed. It does not have any + * failure modes. + */ +void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr); + +/* @} ******************************************************************/ + +/** + * @defgroup talloc_array The talloc array functions + * @ingroup talloc + * + * Talloc contains some handy helpers for handling Arrays conveniently + * + * @{ + */ + +#ifdef DOXYGEN +/** + * @brief Allocate an array. + * + * The macro is equivalent to: + * + * @code + * (type *)talloc_size(ctx, sizeof(type) * count); + * @endcode + * + * except that it provides integer overflow protection for the multiply, + * returning NULL if the multiply overflows. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] type The type that we want to allocate. + * + * @param[in] count The number of 'type' elements you want to allocate. + * + * @return The allocated result, properly cast to 'type *', NULL on + * error. + * + * Example: + * @code + * unsigned int *a, *b; + * a = talloc_zero(NULL, unsigned int); + * b = talloc_array(a, unsigned int, 100); + * @endcode + * + * @see talloc() + * @see talloc_zero_array() + */ +void *talloc_array(const void *ctx, #type, unsigned count); +#else +#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) +void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); +#endif + +#ifdef DOXYGEN +/** + * @brief Allocate an array. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] size The size of an array element. + * + * @param[in] count The number of elements you want to allocate. + * + * @return The allocated result, NULL on error. + */ +void *talloc_array_size(const void *ctx, size_t size, unsigned count); +#else +#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) +#endif + +#ifdef DOXYGEN +/** + * @brief Allocate an array into a typed pointer. + * + * The macro should be used when you have a pointer to an array and want to + * allocate memory of an array to point at with this pointer. When compiling + * with gcc >= 3 it is typesafe. Note this is a wrapper of talloc_array_size() + * and talloc_get_name() will return the current location in the source file + * and not the type. + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] ptr The pointer you want to assign the result to. + * + * @param[in] count The number of elements you want to allocate. + * + * @return The allocated memory chunk, properly casted. NULL on + * error. + */ +void *talloc_array_ptrtype(const void *ctx, const void *ptr, unsigned count); +#else +#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) +#endif + +#ifdef DOXYGEN +/** + * @brief Get the number of elements in a talloc'ed array. + * + * A talloc chunk carries its own size, so for talloc'ed arrays it is not + * necessary to store the number of elements explicitly. + * + * @param[in] ctx The allocated array. + * + * @return The number of elements in ctx. + */ +size_t talloc_array_length(const void *ctx); +#else +#define talloc_array_length(ctx) (talloc_get_size(ctx)/sizeof(*ctx)) +#endif + +#ifdef DOXYGEN +/** + * @brief Allocate a zero-initialized array + * + * @param[in] ctx The talloc context to hang the result off. + * + * @param[in] type The type that we want to allocate. + * + * @param[in] count The number of "type" elements you want to allocate. + * + * @return The allocated result casted to "type *", NULL on error. + * + * The talloc_zero_array() macro is equivalent to: + * + * @code + * ptr = talloc_array(ctx, type, count); + * if (ptr) memset(ptr, 0, sizeof(type) * count); + * @endcode + */ +void *talloc_zero_array(const void *ctx, #type, unsigned count); +#else +#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) +void *_talloc_zero_array(const void *ctx, + size_t el_size, + unsigned count, + const char *name); +#endif + +#ifdef DOXYGEN +/** + * @brief Change the size of a talloc array. + * + * The macro changes the size of a talloc pointer. The 'count' argument is the + * number of elements of type 'type' that you want the resulting pointer to + * hold. + * + * talloc_realloc() has the following equivalences: + * + * @code + * talloc_realloc(ctx, NULL, type, 1) ==> talloc(ctx, type); + * talloc_realloc(ctx, NULL, type, N) ==> talloc_array(ctx, type, N); + * talloc_realloc(ctx, ptr, type, 0) ==> talloc_free(ptr); + * @endcode + * + * The "context" argument is only used if "ptr" is NULL, otherwise it is + * ignored. + * + * @param[in] ctx The parent context used if ptr is NULL. + * + * @param[in] ptr The chunk to be resized. + * + * @param[in] type The type of the array element inside ptr. + * + * @param[in] count The intended number of array elements. + * + * @return The new array, NULL on error. The call will fail either + * due to a lack of memory, or because the pointer has more + * than one parent (see talloc_reference()). + */ +void *talloc_realloc(const void *ctx, void *ptr, #type, size_t count); +#else +#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) +void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); +#endif + +#ifdef DOXYGEN +/** + * @brief Untyped realloc to change the size of a talloc array. + * + * The macro is useful when the type is not known so the typesafe + * talloc_realloc() cannot be used. + * + * @param[in] ctx The parent context used if 'ptr' is NULL. + * + * @param[in] ptr The chunk to be resized. + * + * @param[in] size The new chunk size. + * + * @return The new array, NULL on error. + */ +void *talloc_realloc_size(const void *ctx, void *ptr, size_t size); +#else +#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) +void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); +#endif + +/** + * @brief Provide a function version of talloc_realloc_size. + * + * This is a non-macro version of talloc_realloc(), which is useful as + * libraries sometimes want a ralloc function pointer. A realloc() + * implementation encapsulates the functionality of malloc(), free() and + * realloc() in one call, which is why it is useful to be able to pass around + * a single function pointer. + * + * @param[in] context The parent context used if ptr is NULL. + * + * @param[in] ptr The chunk to be resized. + * + * @param[in] size The new chunk size. + * + * @return The new chunk, NULL on error. + */ +void *talloc_realloc_fn(const void *context, void *ptr, size_t size); + +/* @} ******************************************************************/ + +/** + * @defgroup talloc_string The talloc string functions. + * @ingroup talloc + * + * talloc string allocation and manipulation functions. + * @{ + */ + +/** + * @brief Duplicate a string into a talloc chunk. + * + * This function is equivalent to: + * + * @code + * ptr = talloc_size(ctx, strlen(p)+1); + * if (ptr) memcpy(ptr, p, strlen(p)+1); + * @endcode + * + * This functions sets the name of the new pointer to the passed + * string. This is equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * @param[in] t The talloc context to hang the result off. + * + * @param[in] p The string you want to duplicate. + * + * @return The duplicated string, NULL on error. + */ +char *talloc_strdup(const void *t, const char *p); + +/** + * @brief Append a string to given string. + * + * The destination string is reallocated to take + * strlen(s) + strlen(a) + 1 characters. + * + * This functions sets the name of the new pointer to the new + * string. This is equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * If s == NULL then new context is created. + * + * @param[in] s The destination to append to. + * + * @param[in] a The string you want to append. + * + * @return The concatenated strings, NULL on error. + * + * @see talloc_strdup() + * @see talloc_strdup_append_buffer() + */ +char *talloc_strdup_append(char *s, const char *a); + +/** + * @brief Append a string to a given buffer. + * + * This is a more efficient version of talloc_strdup_append(). It determines the + * length of the destination string by the size of the talloc context. + * + * Use this very carefully as it produces a different result than + * talloc_strdup_append() when a zero character is in the middle of the + * destination string. + * + * @code + * char *str_a = talloc_strdup(NULL, "hello world"); + * char *str_b = talloc_strdup(NULL, "hello world"); + * str_a[5] = str_b[5] = '\0' + * + * char *app = talloc_strdup_append(str_a, ", hello"); + * char *buf = talloc_strdup_append_buffer(str_b, ", hello"); + * + * printf("%s\n", app); // hello, hello (app = "hello, hello") + * printf("%s\n", buf); // hello (buf = "hello\0world, hello") + * @endcode + * + * If s == NULL then new context is created. + * + * @param[in] s The destination buffer to append to. + * + * @param[in] a The string you want to append. + * + * @return The concatenated strings, NULL on error. + * + * @see talloc_strdup() + * @see talloc_strdup_append() + * @see talloc_array_length() + */ +char *talloc_strdup_append_buffer(char *s, const char *a); + +/** + * @brief Duplicate a length-limited string into a talloc chunk. + * + * This function is the talloc equivalent of the C library function strndup(3). + * + * This functions sets the name of the new pointer to the passed string. This is + * equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * @param[in] t The talloc context to hang the result off. + * + * @param[in] p The string you want to duplicate. + * + * @param[in] n The maximum string length to duplicate. + * + * @return The duplicated string, NULL on error. + */ +char *talloc_strndup(const void *t, const char *p, size_t n); + +/** + * @brief Append at most n characters of a string to given string. + * + * The destination string is reallocated to take + * strlen(s) + strnlen(a, n) + 1 characters. + * + * This functions sets the name of the new pointer to the new + * string. This is equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * If s == NULL then new context is created. + * + * @param[in] s The destination string to append to. + * + * @param[in] a The source string you want to append. + * + * @param[in] n The number of characters you want to append from the + * string. + * + * @return The concatenated strings, NULL on error. + * + * @see talloc_strndup() + * @see talloc_strndup_append_buffer() + */ +char *talloc_strndup_append(char *s, const char *a, size_t n); + +/** + * @brief Append at most n characters of a string to given buffer + * + * This is a more efficient version of talloc_strndup_append(). It determines + * the length of the destination string by the size of the talloc context. + * + * Use this very carefully as it produces a different result than + * talloc_strndup_append() when a zero character is in the middle of the + * destination string. + * + * @code + * char *str_a = talloc_strdup(NULL, "hello world"); + * char *str_b = talloc_strdup(NULL, "hello world"); + * str_a[5] = str_b[5] = '\0' + * + * char *app = talloc_strndup_append(str_a, ", hello", 7); + * char *buf = talloc_strndup_append_buffer(str_b, ", hello", 7); + * + * printf("%s\n", app); // hello, hello (app = "hello, hello") + * printf("%s\n", buf); // hello (buf = "hello\0world, hello") + * @endcode + * + * If s == NULL then new context is created. + * + * @param[in] s The destination buffer to append to. + * + * @param[in] a The source string you want to append. + * + * @param[in] n The number of characters you want to append from the + * string. + * + * @return The concatenated strings, NULL on error. + * + * @see talloc_strndup() + * @see talloc_strndup_append() + * @see talloc_array_length() + */ +char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); + +/** + * @brief Format a string given a va_list. + * + * This function is the talloc equivalent of the C library function + * vasprintf(3). + * + * This functions sets the name of the new pointer to the new string. This is + * equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * @param[in] t The talloc context to hang the result off. + * + * @param[in] fmt The format string. + * + * @param[in] ap The parameters used to fill fmt. + * + * @return The formatted string, NULL on error. + */ +char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +/** + * @brief Format a string given a va_list and append it to the given destination + * string. + * + * @param[in] s The destination string to append to. + * + * @param[in] fmt The format string. + * + * @param[in] ap The parameters used to fill fmt. + * + * @return The formatted string, NULL on error. + * + * @see talloc_vasprintf() + */ +char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +/** + * @brief Format a string given a va_list and append it to the given destination + * buffer. + * + * @param[in] s The destination buffer to append to. + * + * @param[in] fmt The format string. + * + * @param[in] ap The parameters used to fill fmt. + * + * @return The formatted string, NULL on error. + * + * @see talloc_vasprintf() + */ +char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); + +/** + * @brief Format a string. + * + * This function is the talloc equivalent of the C library function asprintf(3). + * + * This functions sets the name of the new pointer to the new string. This is + * equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * @param[in] t The talloc context to hang the result off. + * + * @param[in] fmt The format string. + * + * @param[in] ... The parameters used to fill fmt. + * + * @return The formatted string, NULL on error. + */ +char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +/** + * @brief Append a formatted string to another string. + * + * This function appends the given formatted string to the given string. Use + * this variant when the string in the current talloc buffer may have been + * truncated in length. + * + * This functions sets the name of the new pointer to the new + * string. This is equivalent to: + * + * @code + * talloc_set_name_const(ptr, ptr) + * @endcode + * + * If s == NULL then new context is created. + * + * @param[in] s The string to append to. + * + * @param[in] fmt The format string. + * + * @param[in] ... The parameters used to fill fmt. + * + * @return The formatted string, NULL on error. + */ +char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +/** + * @brief Append a formatted string to another string. + * + * This is a more efficient version of talloc_asprintf_append(). It determines + * the length of the destination string by the size of the talloc context. + * + * Use this very carefully as it produces a different result than + * talloc_asprintf_append() when a zero character is in the middle of the + * destination string. + * + * @code + * char *str_a = talloc_strdup(NULL, "hello world"); + * char *str_b = talloc_strdup(NULL, "hello world"); + * str_a[5] = str_b[5] = '\0' + * + * char *app = talloc_asprintf_append(str_a, "%s", ", hello"); + * char *buf = talloc_strdup_append_buffer(str_b, "%s", ", hello"); + * + * printf("%s\n", app); // hello, hello (app = "hello, hello") + * printf("%s\n", buf); // hello (buf = "hello\0world, hello") + * @endcode + * + * If s == NULL then new context is created. + * + * @param[in] s The string to append to + * + * @param[in] fmt The format string. + * + * @param[in] ... The parameters used to fill fmt. + * + * @return The formatted string, NULL on error. + * + * @see talloc_asprintf() + * @see talloc_asprintf_append() + */ +char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); + +/* @} ******************************************************************/ + +/** + * @defgroup talloc_debug The talloc debugging support functions + * @ingroup talloc + * + * To aid memory debugging, talloc contains routines to inspect the currently + * allocated memory hierarchy. + * + * @{ + */ + +/** + * @brief Walk a complete talloc hierarchy. + * + * This provides a more flexible reports than talloc_report(). It + * will recursively call the callback for the entire tree of memory + * referenced by the pointer. References in the tree are passed with + * is_ref = 1 and the pointer that is referenced. + * + * You can pass NULL for the pointer, in which case a report is + * printed for the top level memory context, but only if + * talloc_enable_leak_report() or talloc_enable_leak_report_full() + * has been called. + * + * The recursion is stopped when depth >= max_depth. + * max_depth = -1 means only stop at leaf nodes. + * + * @param[in] ptr The talloc chunk. + * + * @param[in] depth Internal parameter to control recursion. Call with 0. + * + * @param[in] max_depth Maximum recursion level. + * + * @param[in] callback Function to be called on every chunk. + * + * @param[in] private_data Private pointer passed to callback. + */ +void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, + void (*callback)(const void *ptr, + int depth, int max_depth, + int is_ref, + void *private_data), + void *private_data); + +/** + * @brief Print a talloc hierarchy. + * + * This provides a more flexible reports than talloc_report(). It + * will let you specify the depth and max_depth. + * + * @param[in] ptr The talloc chunk. + * + * @param[in] depth Internal parameter to control recursion. Call with 0. + * + * @param[in] max_depth Maximum recursion level. + * + * @param[in] f The file handle to print to. + */ +void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); + +/** + * @brief Print a summary report of all memory used by ptr. + * + * This provides a more detailed report than talloc_report(). It will + * recursively print the entire tree of memory referenced by the + * pointer. References in the tree are shown by giving the name of the + * pointer that is referenced. + * + * You can pass NULL for the pointer, in which case a report is printed + * for the top level memory context, but only if + * talloc_enable_leak_report() or talloc_enable_leak_report_full() has + * been called. + * + * @param[in] ptr The talloc chunk. + * + * @param[in] f The file handle to print to. + * + * Example: + * @code + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * fprintf(stderr, "Dumping memory tree for a:\n"); + * talloc_report_full(a, stderr); + * @endcode + * + * @see talloc_report() + */ +void talloc_report_full(const void *ptr, FILE *f); + +/** + * @brief Print a summary report of all memory used by ptr. + * + * This function prints a summary report of all memory used by ptr. One line of + * report is printed for each immediate child of ptr, showing the total memory + * and number of blocks used by that child. + * + * You can pass NULL for the pointer, in which case a report is printed + * for the top level memory context, but only if talloc_enable_leak_report() + * or talloc_enable_leak_report_full() has been called. + * + * @param[in] ptr The talloc chunk. + * + * @param[in] f The file handle to print to. + * + * Example: + * @code + * unsigned int *a, *b; + * a = talloc(NULL, unsigned int); + * b = talloc(a, unsigned int); + * fprintf(stderr, "Summary of memory tree for a:\n"); + * talloc_report(a, stderr); + * @endcode + * + * @see talloc_report_full() + */ +void talloc_report(const void *ptr, FILE *f); + +/** + * @brief Enable tracking the use of NULL memory contexts. + * + * This enables tracking of the NULL memory context without enabling leak + * reporting on exit. Useful for when you want to do your own leak + * reporting call via talloc_report_null_full(); + */ +void talloc_enable_null_tracking(void); + +/** + * @brief Enable tracking the use of NULL memory contexts. + * + * This enables tracking of the NULL memory context without enabling leak + * reporting on exit. Useful for when you want to do your own leak + * reporting call via talloc_report_null_full(); + */ +void talloc_enable_null_tracking_no_autofree(void); + +/** + * @brief Disable tracking of the NULL memory context. + * + * This disables tracking of the NULL memory context. + */ +void talloc_disable_null_tracking(void); + +/** + * @brief Enable leak report when a program exits. + * + * This enables calling of talloc_report(NULL, stderr) when the program + * exits. In Samba4 this is enabled by using the --leak-report command + * line option. + * + * For it to be useful, this function must be called before any other + * talloc function as it establishes a "null context" that acts as the + * top of the tree. If you don't call this function first then passing + * NULL to talloc_report() or talloc_report_full() won't give you the + * full tree printout. + * + * Here is a typical talloc report: + * + * @code + * talloc report on 'null_context' (total 267 bytes in 15 blocks) + * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + * iconv(UTF8,CP850) contains 42 bytes in 2 blocks + * libcli/auth/spnego_parse.c:55 contains 31 bytes in 2 blocks + * iconv(CP850,UTF8) contains 42 bytes in 2 blocks + * iconv(UTF8,UTF-16LE) contains 45 bytes in 2 blocks + * iconv(UTF-16LE,UTF8) contains 45 bytes in 2 blocks + * @endcode + */ +void talloc_enable_leak_report(void); + +/** + * @brief Enable full leak report when a program exits. + * + * This enables calling of talloc_report_full(NULL, stderr) when the + * program exits. In Samba4 this is enabled by using the + * --leak-report-full command line option. + * + * For it to be useful, this function must be called before any other + * talloc function as it establishes a "null context" that acts as the + * top of the tree. If you don't call this function first then passing + * NULL to talloc_report() or talloc_report_full() won't give you the + * full tree printout. + * + * Here is a typical full report: + * + * @code + * full talloc report on 'root' (total 18 bytes in 8 blocks) + * p1 contains 18 bytes in 7 blocks (ref 0) + * r1 contains 13 bytes in 2 blocks (ref 0) + * reference to: p2 + * p2 contains 1 bytes in 1 blocks (ref 1) + * x3 contains 1 bytes in 1 blocks (ref 0) + * x2 contains 1 bytes in 1 blocks (ref 0) + * x1 contains 1 bytes in 1 blocks (ref 0) + * @endcode + */ +void talloc_enable_leak_report_full(void); + +/** + * @brief Set a custom "abort" function that is called on serious error. + * + * The default "abort" function is abort(). + * + * The "abort" function is called when: + * + *
    + *
  • talloc_get_type_abort() fails
  • + *
  • the provided pointer is not a valid talloc context
  • + *
  • when the context meta data are invalid
  • + *
  • when access after free is detected
  • + *
+ * + * Example: + * + * @code + * void my_abort(const char *reason) + * { + * fprintf(stderr, "talloc abort: %s\n", reason); + * abort(); + * } + * + * talloc_set_abort_fn(my_abort); + * @endcode + * + * @param[in] abort_fn The new "abort" function. + * + * @see talloc_set_log_fn() + * @see talloc_get_type() + */ +void talloc_set_abort_fn(void (*abort_fn)(const char *reason)); + +/** + * @brief Set a logging function. + * + * @param[in] log_fn The logging function. + * + * @see talloc_set_log_stderr() + * @see talloc_set_abort_fn() + */ +void talloc_set_log_fn(void (*log_fn)(const char *message)); + +/** + * @brief Set stderr as the output for logs. + * + * @see talloc_set_log_fn() + * @see talloc_set_abort_fn() + */ +void talloc_set_log_stderr(void); + +/** + * @brief Set a max memory limit for the current context hierarchy + * This affects all children of this context and constrain any + * allocation in the hierarchy to never exceed the limit set. + * The limit can be removed by setting 0 (unlimited) as the + * max_size by calling the function again on the same context. + * Memory limits can also be nested, meaning a child can have + * a stricter memory limit than a parent. + * Memory limits are enforced only at memory allocation time. + * Stealing a context into a 'limited' hierarchy properly + * updates memory usage but does *not* cause failure if the + * move causes the new parent to exceed its limits. However + * any further allocation on that hierarchy will then fail. + * + * @param[in] ctx The talloc context to set the limit on + * @param[in] max_size The (new) max_size + */ +int talloc_set_memlimit(const void *ctx, size_t max_size); + +/* @} ******************************************************************/ + +#if TALLOC_DEPRECATED +#define talloc_zero_p(ctx, type) talloc_zero(ctx, type) +#define talloc_p(ctx, type) talloc(ctx, type) +#define talloc_array_p(ctx, type, count) talloc_array(ctx, type, count) +#define talloc_realloc_p(ctx, p, type, count) talloc_realloc(ctx, p, type, count) +#define talloc_destroy(ctx) talloc_free(ctx) +#define talloc_append_string(c, s, a) (s?talloc_strdup_append(s,a):talloc_strdup(c, a)) +#endif + +#ifndef TALLOC_MAX_DEPTH +#define TALLOC_MAX_DEPTH 10000 +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/CMakeLists.txt b/app/src/main/cpp/virglrenderer/CMakeLists.txt new file mode 100644 index 0000000..6741df0 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.22.1) + +project(VirGLRenderer) + +set(CMAKE_C_FLAGS "-O2 -Wno-unused-function -Wimplicit-function-declaration") + +include_directories(src + src/gallium/include + src/gallium/auxiliary + src/gallium/auxiliary/util + server) + +add_library(virglrenderer SHARED + src/iov.c + src/vrend_blitter.c + src/vrend_decode.c + src/vrend_formats.c + src/vrend_object.c + src/vrend_renderer.c + src/vrend_shader.c + server/virgl_server.c + server/virgl_server_shm.c + server/virgl_server_renderer.c + src/gallium/auxiliary/util/u_format.c + src/gallium/auxiliary/util/u_format_table.c + src/gallium/auxiliary/util/u_texture.c + src/gallium/auxiliary/util/u_hash_table.c + src/gallium/auxiliary/util/u_debug.c + src/gallium/auxiliary/util/u_cpu_detect.c + src/gallium/auxiliary/util/u_bitmask.c + src/gallium/auxiliary/util/u_surface.c + src/gallium/auxiliary/util/u_math.c + src/gallium/auxiliary/util/u_debug_describe.c + src/gallium/auxiliary/cso_cache/cso_cache.c + src/gallium/auxiliary/cso_cache/cso_hash.c + src/gallium/auxiliary/tgsi/tgsi_dump.c + src/gallium/auxiliary/tgsi/tgsi_ureg.c + src/gallium/auxiliary/tgsi/tgsi_build.c + src/gallium/auxiliary/tgsi/tgsi_scan.c + src/gallium/auxiliary/tgsi/tgsi_info.c + src/gallium/auxiliary/tgsi/tgsi_parse.c + src/gallium/auxiliary/tgsi/tgsi_text.c + src/gallium/auxiliary/tgsi/tgsi_strings.c + src/gallium/auxiliary/tgsi/tgsi_sanity.c + src/gallium/auxiliary/tgsi/tgsi_iterate.c + src/gallium/auxiliary/tgsi/tgsi_util.c + src/gallium/auxiliary/tgsi/tgsi_transform.c + src/gallium/auxiliary/os/os_misc.c) + +target_link_libraries(virglrenderer + log + android + EGL + GLESv2 + GLESv3) \ No newline at end of file diff --git a/app/src/main/cpp/virglrenderer/server/virgl_server.c b/app/src/main/cpp/virglrenderer/server/virgl_server.c new file mode 100644 index 0000000..d26d3f9 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/server/virgl_server.c @@ -0,0 +1,147 @@ +/************************************************************************** + * + * Copyright (C) 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/u_memory.h" +#include "virgl_server.h" +#include "virgl_server_protocol.h" + +struct jni_info jni_info; + +static struct virgl_client *virgl_server_handle_new_connection(int fd) +{ + struct virgl_client *client = calloc(1, sizeof(struct virgl_client)); + client->fd = fd; + client->initialized = false; + return client; +} + +static void virgl_server_kill_connection(struct virgl_client *client) +{ + (*jni_info.env)->CallVoidMethod(jni_info.env, jni_info.obj, jni_info.kill_connection, client->fd); +} + +static void virgl_server_destroy_client(struct virgl_client **client) +{ + virgl_server_destroy_renderer(*client); + + free(*client); + *client = NULL; +} + +static void virgl_server_handle_request(struct virgl_client *client) +{ + int ret; + uint32_t header[2]; + + ret = virgl_block_read(client->fd, &header, sizeof(header)); + if (ret < 0 || (size_t)ret < sizeof(header)) { + virgl_server_kill_connection(client); + return; + } + + if (!client->initialized) { + if (header[1] != VCMD_CREATE_RENDERER) { + virgl_server_kill_connection(client); + return; + } + + ret = virgl_server_create_renderer(client, header[0]); + client->initialized = true; + } + + vrend_renderer_check_fences(client); + + switch (header[1]) { + case VCMD_GET_CAPS: + ret = virgl_server_send_caps(client, header[0]); + break; + case VCMD_RESOURCE_CREATE: + ret = virgl_server_resource_create(client, header[0]); + break; + case VCMD_RESOURCE_DESTROY: + ret = virgl_server_resource_destroy(client, header[0]); + break; + case VCMD_TRANSFER_GET: + ret = virgl_server_transfer_get(client, header[0]); + break; + case VCMD_TRANSFER_PUT: + ret = virgl_server_transfer_put(client, header[0]); + break; + case VCMD_SUBMIT_CMD: + ret = virgl_server_submit_cmd(client, header[0]); + break; + case VCMD_RESOURCE_BUSY_WAIT: + ret = virgl_server_resource_busy_wait(client, header[0]); + break; + case VCMD_FLUSH_FRONTBUFFER: + ret = virgl_server_flush_frontbuffer(client, header[0]); + break; + } + + if (ret < 0) + virgl_server_kill_connection(client); +} + +JNIEXPORT jlong JNICALL +Java_com_winlator_xenvironment_components_VirGLRendererComponent_handleNewConnection(JNIEnv *env, jobject obj, jint fd) { + jni_info.env = env; + jni_info.obj = obj; + + jclass cls = (*env)->GetObjectClass(env, obj); + jni_info.kill_connection = (*env)->GetMethodID(env, cls, "killConnection", "(I)V"); + jni_info.get_shared_egl_context = (*env)->GetMethodID(env, cls, "getSharedEGLContext", "()J"); + jni_info.flush_frontbuffer = (*env)->GetMethodID(env, cls, "flushFrontbuffer", "(II)V"); + + return (jlong)virgl_server_handle_new_connection(fd); +} + +JNIEXPORT void JNICALL +Java_com_winlator_xenvironment_components_VirGLRendererComponent_handleRequest(JNIEnv *env, jobject obj, jlong clientPtr) { + jni_info.env = env; + jni_info.obj = obj; + virgl_server_handle_request((struct virgl_client*)clientPtr); +} + +JNIEXPORT void JNICALL +Java_com_winlator_xenvironment_components_VirGLRendererComponent_destroyClient(JNIEnv *env, jobject obj, jlong clientPtr) { + struct virgl_client *client = (struct virgl_client*)clientPtr; + virgl_server_destroy_client(&client); +} + +JNIEXPORT void JNICALL +Java_com_winlator_xenvironment_components_VirGLRendererComponent_destroyRenderer(JNIEnv *env, jobject obj, jlong clientPtr) { + virgl_server_destroy_renderer((struct virgl_client*)clientPtr); +} \ No newline at end of file diff --git a/app/src/main/cpp/virglrenderer/server/virgl_server.h b/app/src/main/cpp/virglrenderer/server/virgl_server.h new file mode 100644 index 0000000..3c9c888 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/server/virgl_server.h @@ -0,0 +1,90 @@ +/************************************************************************** + * + * Copyright (C) 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef VIRGL_SERVER_H +#define VIRGL_SERVER_H + +#include +#include +#include +#include + +#include "vrend_renderer.h" + +#include +#include + +#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "System.out", __VA_ARGS__); + +struct jni_info { + jobject obj; + JNIEnv *env; + jmethodID kill_connection; + jmethodID get_shared_egl_context; + jmethodID flush_frontbuffer; +}; + +struct virgl_server_renderer { + struct util_hash_table *iovec_hash; + GLuint framebuffer; + int handle; + int ctx_id; + int fence_id; + int last_fence_id; + + EGLDisplay egl_display; + EGLConfig egl_conf; + EGLContext egl_ctx; +}; + +struct virgl_client { + int fd; + struct virgl_server_renderer *renderer; + struct vrend_state *vrend_state; + struct util_hash_table *res_hash; + struct vrend_decode_ctx *dec_ctx[VREND_MAX_CTX]; + struct vrend_blitter_ctx *vrend_blit_ctx; + bool initialized; +}; + +extern struct jni_info jni_info; + +int virgl_server_create_renderer(struct virgl_client *client, uint32_t length); +int virgl_server_send_caps(struct virgl_client *client, uint32_t length); +int virgl_server_resource_create(struct virgl_client *client, uint32_t length); +int virgl_server_resource_destroy(struct virgl_client *client, uint32_t length); +int virgl_server_transfer_get(struct virgl_client *client, uint32_t length); +int virgl_server_transfer_put(struct virgl_client *client, uint32_t length); +int virgl_server_submit_cmd(struct virgl_client *client, uint32_t length); +int virgl_server_resource_busy_wait(struct virgl_client *client, uint32_t length); +int virgl_server_flush_frontbuffer(struct virgl_client *client, uint32_t length); + +int virgl_block_read(int fd, void *buf, int size); + +int virgl_server_renderer_create_fence(struct virgl_client *client); + +void virgl_server_destroy_renderer(struct virgl_client *client); + +#endif + diff --git a/app/src/main/cpp/virglrenderer/server/virgl_server_protocol.h b/app/src/main/cpp/virglrenderer/server/virgl_server_protocol.h new file mode 100644 index 0000000..6c86f1c --- /dev/null +++ b/app/src/main/cpp/virglrenderer/server/virgl_server_protocol.h @@ -0,0 +1,38 @@ +/* + * Copyright 2014, 2015 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VIRGL_SERVER_PROTOCOL_H +#define VIRGL_SERVER_PROTOCOL_H + +#define VCMD_CREATE_RENDERER 1 +#define VCMD_GET_CAPS 2 +#define VCMD_RESOURCE_CREATE 3 +#define VCMD_RESOURCE_DESTROY 4 +#define VCMD_TRANSFER_GET 5 +#define VCMD_TRANSFER_PUT 6 +#define VCMD_SUBMIT_CMD 7 +#define VCMD_RESOURCE_BUSY_WAIT 8 +#define VCMD_FLUSH_FRONTBUFFER 9 + +#define VCMD_BUSY_WAIT_FLAG_WAIT 1 + +#endif diff --git a/app/src/main/cpp/virglrenderer/server/virgl_server_renderer.c b/app/src/main/cpp/virglrenderer/server/virgl_server_renderer.c new file mode 100644 index 0000000..79a77db --- /dev/null +++ b/app/src/main/cpp/virglrenderer/server/virgl_server_renderer.c @@ -0,0 +1,590 @@ +/************************************************************************** + * + * Copyright (C) 2015 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "virgl_hw.h" + +#include +#include +#include + +#include + +#include "virgl_server.h" +#include "virgl_server_shm.h" +#include "virgl_server_protocol.h" + +#include "util/u_debug.h" +#include "util/u_math.h" +#include "util/u_memory.h" +#include "util/u_hash_table.h" + +#include + +static void virgl_server_write_fence(struct virgl_client *client, uint32_t fence_id) +{ + client->renderer->last_fence_id = fence_id; +} + +static virgl_gl_context virgl_server_egl_create_context(struct virgl_client *client) +{ + struct virgl_server_renderer *renderer = client->renderer; + EGLContext egl_ctx; + EGLint ctx_att[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + + egl_ctx = eglCreateContext(renderer->egl_display, renderer->egl_conf, renderer->egl_ctx, ctx_att); + + return (virgl_gl_context)egl_ctx; +} + +static void virgl_server_egl_destroy_context(struct virgl_client *client, virgl_gl_context ctx) +{ + eglDestroyContext(client->renderer->egl_display, (EGLContext)ctx); +} + +static int virgl_server_egl_make_current(struct virgl_client *client, virgl_gl_context ctx) +{ + return eglMakeCurrent(client->renderer->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, (EGLContext)ctx); +} + +struct vrend_if_cbs virgl_server_cbs = { + .write_fence = virgl_server_write_fence, + .create_gl_context = virgl_server_egl_create_context, + .destroy_gl_context = virgl_server_egl_destroy_context, + .make_current = virgl_server_egl_make_current, +}; + +static bool virgl_server_egl_init(struct virgl_server_renderer *renderer) +{ + static EGLint conf_att[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_NONE, + }; + static const EGLint ctx_att[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + + EGLBoolean success; + EGLint major, minor, num_configs; + + renderer->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (!renderer->egl_display) + return false; + + success = eglInitialize(renderer->egl_display, &major, &minor); + if (!success) + return false; + + success = eglBindAPI(EGL_OPENGL_ES_API); + if (!success) + return false; + + success = eglChooseConfig(renderer->egl_display, conf_att, &renderer->egl_conf, + 1, &num_configs); + + if (!success || num_configs != 1) + return false; + + jlong shared_egl_ctx_ptr = (*jni_info.env)->CallLongMethod(jni_info.env, jni_info.obj, jni_info.get_shared_egl_context); + EGLContext shared_egl_ctx = (EGLContext)shared_egl_ctx_ptr; + + renderer->egl_ctx = eglCreateContext(renderer->egl_display, + renderer->egl_conf, + shared_egl_ctx ? shared_egl_ctx : EGL_NO_CONTEXT, + ctx_att); + + eglMakeCurrent(renderer->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, renderer->egl_ctx); + if (!renderer->egl_ctx) + return false; + + return true; +} + +static unsigned +hash_func(void *key) +{ + intptr_t ip = pointer_to_intptr(key); + return (unsigned)(ip & 0xffffffff); +} + +static int +compare_iovecs(void *key1, void *key2) +{ + if (key1 < key2) { + return -1; + } else if (key1 > key2) { + return 1; + } else { + return 0; + } +} + +static void free_iovec(void *value) +{ + struct iovec *iovec = value; + if (iovec->iov_base) + munmap(iovec->iov_base, iovec->iov_len); + free(iovec); +} + +static int virgl_block_write(int fd, void *buf, int size) +{ + char *ptr = buf; + int left; + int ret; + left = size; + + do { + ret = write(fd, ptr, left); + if (ret < 0) + return -errno; + + left -= ret; + ptr += ret; + } while (left); + + return size; +} + +int virgl_block_read(int fd, void *buf, int size) +{ + char *ptr = buf; + int left; + int ret; + + left = size; + do { + ret = read(fd, ptr, left); + if (ret <= 0) + return ret == -1 ? -errno : 0; + + left -= ret; + ptr += ret; + } while (left); + + return size; +} + +static int virgl_server_send_fd(int sock_fd, int fd) +{ + struct iovec iovec; + char buf[CMSG_SPACE(sizeof(int))], c; + struct msghdr msgh = { 0 }; + memset(buf, 0, sizeof(buf)); + + iovec.iov_base = &c; + iovec.iov_len = sizeof(char); + + msgh.msg_name = NULL; + msgh.msg_namelen = 0; + msgh.msg_iov = &iovec; + msgh.msg_iovlen = 1; + msgh.msg_control = buf; + msgh.msg_controllen = sizeof(buf); + msgh.msg_flags = 0; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + + *((int *)CMSG_DATA(cmsg)) = fd; + + int size = sendmsg(sock_fd, &msgh, 0); + if (size < 0) + return -EINVAL; + + return 0; +} + +int virgl_server_create_renderer(struct virgl_client *client, uint32_t length) +{ + int ret; + + struct virgl_server_renderer *renderer = calloc(1, sizeof(struct virgl_server_renderer)); + renderer->iovec_hash = util_hash_table_create(hash_func, compare_iovecs, free_iovec); + renderer->ctx_id = 1; + + client->renderer = renderer; + + virgl_server_egl_init(renderer); + + ret = vrend_renderer_init(client, &virgl_server_cbs); + if (ret) + return -1; + + ret = vrend_renderer_context_create(client, renderer->ctx_id); + return ret; +} + +void virgl_server_destroy_renderer(struct virgl_client *client) +{ + if (!client->initialized) + return; + + if (client->renderer->framebuffer) + glDeleteFramebuffers(1, &client->renderer->framebuffer); + + vrend_renderer_context_destroy(client, client->renderer->ctx_id); + vrend_renderer_fini(client); + util_hash_table_destroy(client->renderer->iovec_hash); + client->renderer->iovec_hash = NULL; + + free(client->renderer); + client->renderer = NULL; + client->initialized = false; +} + +int virgl_server_send_caps(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t send_buf[2]; + void *caps_buf; + int ret; + uint32_t max_ver, max_size; + + vrend_renderer_get_cap_set(2, &max_ver, &max_size); + + if (max_size == 0) + return -1; + + caps_buf = malloc(max_size); + if (!caps_buf) + return -1; + + vrend_renderer_fill_caps(client, 2, 1, caps_buf); + + send_buf[0] = max_size + 1; + send_buf[1] = 2; + ret = virgl_block_write(client->fd, send_buf, 8); + if (ret < 0) + goto end; + + virgl_block_write(client->fd, caps_buf, max_size); + +end: + free(caps_buf); + return 0; +} + +int virgl_server_resource_create(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t recv_buf[11]; + struct vrend_renderer_resource_create_args args; + struct iovec *iovec; + int ret, fd; + + ret = virgl_block_read(client->fd, &recv_buf, sizeof(recv_buf)); + if (ret != sizeof(recv_buf)) + return -1; + + args.handle = recv_buf[0]; + args.target = recv_buf[1]; + args.format = recv_buf[2]; + args.bind = recv_buf[3]; + args.width = recv_buf[4]; + args.height = recv_buf[5]; + args.depth = recv_buf[6]; + args.array_size = recv_buf[7]; + args.last_level = recv_buf[8]; + args.nr_samples = recv_buf[9]; + args.flags = 0; + + if (util_hash_table_get(client->renderer->iovec_hash, intptr_to_pointer(args.handle))) + return -EEXIST; + + ret = vrend_renderer_resource_create(client, &args, NULL, 0); + if (ret) + return ret; + + vrend_renderer_attach_res_ctx(client, client->renderer->ctx_id, args.handle); + + iovec = CALLOC_STRUCT(iovec); + if (!iovec) + return -ENOMEM; + + iovec->iov_len = recv_buf[10]; + + if (iovec->iov_len == 0) { + iovec->iov_base = NULL; + goto out; + } + + fd = virgl_server_new_shm(args.handle, iovec->iov_len); + if (fd < 0) { + FREE(iovec); + return fd; + } + + iovec->iov_base = mmap(NULL, iovec->iov_len, PROT_WRITE | PROT_READ, + MAP_SHARED, fd, 0); + + if (iovec->iov_base == MAP_FAILED) { + close(fd); + FREE(iovec); + return -ENOMEM; + } + + ret = virgl_server_send_fd(client->fd, fd); + if (ret < 0) { + close(fd); + munmap(iovec->iov_base, iovec->iov_len); + return ret; + } + + close(fd); + +out: + vrend_renderer_resource_attach_iov(client, args.handle, iovec, 1); + util_hash_table_set(client->renderer->iovec_hash, intptr_to_pointer(args.handle), iovec); + return 0; +} + +int virgl_server_resource_destroy(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t recv_buf[1]; + int ret; + uint32_t handle; + + ret = virgl_block_read(client->fd, &recv_buf, sizeof(recv_buf)); + if (ret != sizeof(recv_buf)) + return -1; + + handle = recv_buf[0]; + vrend_renderer_attach_res_ctx(client, client->renderer->ctx_id, handle); + + vrend_renderer_resource_detach_iov(client, handle, NULL, NULL); + util_hash_table_remove(client->renderer->iovec_hash, intptr_to_pointer(handle)); + + vrend_renderer_resource_unref(client, handle); + return 0; +} + +int virgl_server_transfer_get(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t recv_buf[10]; + int ret; + struct pipe_box box; + struct iovec *iovec; + struct vrend_transfer_info transfer_info; + + ret = virgl_block_read(client->fd, &recv_buf, sizeof(recv_buf)); + if (ret != sizeof(recv_buf)) + return ret; + + box.x = recv_buf[2]; + box.y = recv_buf[3]; + box.z = recv_buf[4]; + box.width = recv_buf[5]; + box.height = recv_buf[6]; + box.depth = recv_buf[7]; + + transfer_info.handle = recv_buf[0]; + transfer_info.ctx_id = client->renderer->ctx_id; + transfer_info.level = recv_buf[1]; + transfer_info.stride = 0; + transfer_info.layer_stride = 0; + transfer_info.box = &box; + transfer_info.offset = recv_buf[9]; + transfer_info.iovec = NULL; + transfer_info.iovec_cnt = 0; + transfer_info.context0 = true; + transfer_info.synchronized = false; + + iovec = util_hash_table_get(client->renderer->iovec_hash, intptr_to_pointer(transfer_info.handle)); + if (!iovec) + return -ESRCH; + + if (transfer_info.offset >= iovec->iov_len) + return -EFAULT; + + ret = vrend_renderer_transfer_iov(client, &transfer_info, VIRGL_TRANSFER_FROM_HOST); + + if (ret) + return ret; + + return 0; +} + +int virgl_server_transfer_put(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t recv_buf[10]; + int ret; + struct pipe_box box; + struct iovec *iovec; + struct vrend_transfer_info transfer_info; + + ret = virgl_block_read(client->fd, &recv_buf, sizeof(recv_buf)); + if (ret != sizeof(recv_buf)) + return ret; + + box.x = recv_buf[2]; + box.y = recv_buf[3]; + box.z = recv_buf[4]; + box.width = recv_buf[5]; + box.height = recv_buf[6]; + box.depth = recv_buf[7]; + + transfer_info.handle = recv_buf[0]; + transfer_info.ctx_id = client->renderer->ctx_id; + transfer_info.level = recv_buf[1]; + transfer_info.stride = 0; + transfer_info.layer_stride = 0; + transfer_info.box = &box; + transfer_info.offset = recv_buf[9]; + transfer_info.iovec = NULL; + transfer_info.iovec_cnt = 0; + transfer_info.context0 = true; + transfer_info.synchronized = false; + + iovec = util_hash_table_get(client->renderer->iovec_hash, intptr_to_pointer(transfer_info.handle)); + if (!iovec) + return -ESRCH; + + vrend_renderer_transfer_iov(client, &transfer_info, VIRGL_TRANSFER_TO_HOST); + + if (ret) + return ret; + + return 0; +} + +int virgl_server_submit_cmd(struct virgl_client *client, uint32_t length) +{ + uint32_t *cbuf; + int cbuf_len, ret; + + cbuf_len = length * 4; + cbuf = malloc(cbuf_len); + if (!cbuf) + return -1; + + ret = virgl_block_read(client->fd, cbuf, cbuf_len); + if (ret != cbuf_len) { + free(cbuf); + return -1; + } + + vrend_decode_block(client, client->renderer->ctx_id, cbuf, length); + + free(cbuf); + virgl_server_renderer_create_fence(client); + return 0; +} + +int virgl_server_resource_busy_wait(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t recv_buf[2]; + uint32_t send_buf[3]; + int ret; + int flags; + bool busy = false; + + ret = virgl_block_read(client->fd, &recv_buf, sizeof(recv_buf)); + if (ret != sizeof(recv_buf)) + return -1; + + flags = recv_buf[1]; + + do { + busy = client->renderer->last_fence_id != client->renderer->fence_id; + if (!busy || !(flags & VCMD_BUSY_WAIT_FLAG_WAIT)) + break; + + vrend_renderer_check_fences(client); + } while (1); + + send_buf[0] = 1; + send_buf[1] = VCMD_RESOURCE_BUSY_WAIT; + send_buf[2] = busy ? 1 : 0; + + ret = virgl_block_write(client->fd, send_buf, sizeof(send_buf)); + if (ret < 0) + return ret; + + return 0; +} + +int virgl_server_flush_frontbuffer(struct virgl_client *client, UNUSED uint32_t length) +{ + uint32_t recv_buf[2]; + uint32_t handle, drawable; + int ret; + + ret = virgl_block_read(client->fd, &recv_buf, sizeof(recv_buf)); + if (ret != sizeof(recv_buf)) + return -1; + + handle = recv_buf[0]; + drawable = recv_buf[1]; + + if (handle != client->renderer->handle) { + struct vrend_context *ctx = vrend_lookup_renderer_ctx(client, client->renderer->ctx_id); + struct vrend_resource *res = vrend_renderer_ctx_res_lookup(ctx, handle); + + if (client->renderer->framebuffer) + glDeleteFramebuffers(1, &client->renderer->framebuffer); + + GLuint framebuffer; + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + vrend_fb_bind_texture(res, 0, 0, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + client->renderer->framebuffer = framebuffer; + client->renderer->handle = handle; + } + + (*jni_info.env)->CallVoidMethod(jni_info.env, jni_info.obj, jni_info.flush_frontbuffer, drawable, client->renderer->framebuffer); + return 0; +} + +int virgl_server_renderer_create_fence(struct virgl_client *client) +{ + vrend_renderer_create_fence(client, ++client->renderer->fence_id, 0); + return 0; +} + +JNIEXPORT jlong JNICALL +Java_com_winlator_xenvironment_components_VirGLRendererComponent_getCurrentEGLContextPtr(JNIEnv *env, jobject obj) { + EGLContext egl_ctx = eglGetCurrentContext(); + return egl_ctx != EGL_NO_CONTEXT ? (jlong)egl_ctx : 0; +} \ No newline at end of file diff --git a/app/src/main/cpp/virglrenderer/server/virgl_server_shm.c b/app/src/main/cpp/virglrenderer/server/virgl_server_shm.c new file mode 100644 index 0000000..3ef2cf6 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/server/virgl_server_shm.c @@ -0,0 +1,63 @@ +/************************************************************************** + * + * Copyright (C) 2018 Chromium. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "virgl_server_shm.h" + +#include +#include +#include +#include + +#include +#include + +static int memfd_create(const char *name, unsigned int flags) +{ +#ifdef __NR_memfd_create + return syscall(__NR_memfd_create, name, flags); +#else + return -1; +#endif +} + +int virgl_server_new_shm(uint32_t handle, size_t size) +{ + int fd, ret; + int length = snprintf(NULL, 0, "virgl-res-%u", handle); + char *str = malloc(length + 1); + snprintf(str, length + 1, "virgl-res-%u", handle); + + fd = memfd_create(str, MFD_ALLOW_SEALING); + free(str); + if (fd < 0) + return -errno; + + ret = ftruncate(fd, size); + if (ret < 0) { + close(fd); + return -errno; + } + + return fd; +} diff --git a/app/src/main/cpp/virglrenderer/server/virgl_server_shm.h b/app/src/main/cpp/virglrenderer/server/virgl_server_shm.h new file mode 100644 index 0000000..6e05dab --- /dev/null +++ b/app/src/main/cpp/virglrenderer/server/virgl_server_shm.h @@ -0,0 +1,39 @@ +#ifndef VIRGL_SERVER_SHM_H +#define VIRGL_SERVER_SHM_H + +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef MFD_HUGETLB +#define MFD_HUGETLB 0x0004U +#endif + +#ifndef MFD_HUGE_SHIFT +#define MFD_HUGE_SHIFT 26 +#endif + +#include +#include + +int virgl_server_new_shm(uint32_t handle, size_t size); + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_cache.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_cache.c new file mode 100644 index 0000000..232fa9e --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_cache.c @@ -0,0 +1,322 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/* Authors: Zack Rusin + */ + +#include "util/u_debug.h" + +#include "util/u_memory.h" + +#include "cso_cache.h" +#include "cso_hash.h" + + +struct cso_cache { + struct cso_hash *hashes[CSO_CACHE_MAX]; + int max_size; + + cso_sanitize_callback sanitize_cb; + void *sanitize_data; +}; + +#if 1 +static unsigned hash_key(const void *key, unsigned key_size) +{ + unsigned *ikey = (unsigned *)key; + unsigned hash = 0, i; + + assert(key_size % 4 == 0); + + /* I'm sure this can be improved on: + */ + for (i = 0; i < key_size/4; i++) + hash ^= ikey[i]; + + return hash; +} +#else +static unsigned hash_key(const unsigned char *p, int n) +{ + unsigned h = 0; + unsigned g; + + while (n--) { + h = (h << 4) + *p++; + if ((g = (h & 0xf0000000)) != 0) + h ^= g >> 23; + h &= ~g; + } + return h; +} +#endif + +unsigned cso_construct_key(void *item, int item_size) +{ + return hash_key((item), item_size); +} + +static inline struct cso_hash *_cso_hash_for_type(struct cso_cache *sc, enum cso_cache_type type) +{ + struct cso_hash *hash; + hash = sc->hashes[type]; + return hash; +} + +static void delete_blend_state(void *state, UNUSED void *data) +{ + struct cso_blend *cso = (struct cso_blend *)state; + if (cso->delete_state) + cso->delete_state(cso->context, cso->data); + FREE(state); +} + +static void delete_depth_stencil_state(void *state, UNUSED void *data) +{ + struct cso_depth_stencil_alpha *cso = (struct cso_depth_stencil_alpha *)state; + if (cso->delete_state) + cso->delete_state(cso->context, cso->data); + FREE(state); +} + +static void delete_sampler_state(void *state, UNUSED void *data) +{ + struct cso_sampler *cso = (struct cso_sampler *)state; + if (cso->delete_state) + cso->delete_state(cso->context, cso->data); + FREE(state); +} + +static void delete_rasterizer_state(void *state, UNUSED void *data) +{ + struct cso_rasterizer *cso = (struct cso_rasterizer *)state; + if (cso->delete_state) + cso->delete_state(cso->context, cso->data); + FREE(state); +} + +static void delete_velements(void *state, UNUSED void *data) +{ + struct cso_velements *cso = (struct cso_velements *)state; + if (cso->delete_state) + cso->delete_state(cso->context, cso->data); + FREE(state); +} + +static inline void delete_cso(void *state, enum cso_cache_type type) +{ + switch (type) { + case CSO_BLEND: + delete_blend_state(state, 0); + break; + case CSO_SAMPLER: + delete_sampler_state(state, 0); + break; + case CSO_DEPTH_STENCIL_ALPHA: + delete_depth_stencil_state(state, 0); + break; + case CSO_RASTERIZER: + delete_rasterizer_state(state, 0); + break; + case CSO_VELEMENTS: + delete_velements(state, 0); + break; + default: + assert(0); + FREE(state); + } +} + + +static inline void sanitize_hash(struct cso_cache *sc, + struct cso_hash *hash, + enum cso_cache_type type, + int max_size) +{ + if (sc->sanitize_cb) + sc->sanitize_cb(hash, type, max_size, sc->sanitize_data); +} + + +static inline void sanitize_cb(struct cso_hash *hash, enum cso_cache_type type, + int max_size, UNUSED void *user_data) +{ + /* if we're approach the maximum size, remove fourth of the entries + * otherwise every subsequent call will go through the same */ + int hash_size = cso_hash_size(hash); + int max_entries = (max_size > hash_size) ? max_size : hash_size; + int to_remove = (max_size < max_entries) * max_entries/4; + if (hash_size > max_size) + to_remove += hash_size - max_size; + while (to_remove) { + /*remove elements until we're good */ + /*fixme: currently we pick the nodes to remove at random*/ + struct cso_hash_iter iter = cso_hash_first_node(hash); + void *cso = cso_hash_take(hash, cso_hash_iter_key(iter)); + delete_cso(cso, type); + --to_remove; + } +} + +struct cso_hash_iter +cso_insert_state(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type, + void *state) +{ + struct cso_hash *hash = _cso_hash_for_type(sc, type); + sanitize_hash(sc, hash, type, sc->max_size); + + return cso_hash_insert(hash, hash_key, state); +} + +struct cso_hash_iter +cso_find_state(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type) +{ + struct cso_hash *hash = _cso_hash_for_type(sc, type); + + return cso_hash_find(hash, hash_key); +} + + +void *cso_hash_find_data_from_template( struct cso_hash *hash, + unsigned hash_key, + void *templ, + int size ) +{ + struct cso_hash_iter iter = cso_hash_find(hash, hash_key); + while (!cso_hash_iter_is_null(iter)) { + void *iter_data = cso_hash_iter_data(iter); + if (!memcmp(iter_data, templ, size)) { + /* We found a match + */ + return iter_data; + } + iter = cso_hash_iter_next(iter); + } + return NULL; +} + + +struct cso_hash_iter cso_find_state_template(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type, + void *templ, unsigned size) +{ + struct cso_hash_iter iter = cso_find_state(sc, hash_key, type); + while (!cso_hash_iter_is_null(iter)) { + void *iter_data = cso_hash_iter_data(iter); + if (!memcmp(iter_data, templ, size)) + return iter; + iter = cso_hash_iter_next(iter); + } + return iter; +} + +void * cso_take_state(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type) +{ + struct cso_hash *hash = _cso_hash_for_type(sc, type); + return cso_hash_take(hash, hash_key); +} + +struct cso_cache *cso_cache_create(void) +{ + struct cso_cache *sc = MALLOC_STRUCT(cso_cache); + int i; + if (sc == NULL) + return NULL; + + sc->max_size = 4096; + for (i = 0; i < CSO_CACHE_MAX; i++) + sc->hashes[i] = cso_hash_create(); + + sc->sanitize_cb = sanitize_cb; + sc->sanitize_data = 0; + + return sc; +} + +void cso_for_each_state(struct cso_cache *sc, enum cso_cache_type type, + cso_state_callback func, void *user_data) +{ + struct cso_hash *hash = _cso_hash_for_type(sc, type); + struct cso_hash_iter iter; + + iter = cso_hash_first_node(hash); + while (!cso_hash_iter_is_null(iter)) { + void *state = cso_hash_iter_data(iter); + iter = cso_hash_iter_next(iter); + if (state) { + func(state, user_data); + } + } +} + +void cso_cache_delete(struct cso_cache *sc) +{ + int i; + assert(sc); + + if (!sc) + return; + + /* delete driver data */ + cso_for_each_state(sc, CSO_BLEND, delete_blend_state, 0); + cso_for_each_state(sc, CSO_DEPTH_STENCIL_ALPHA, delete_depth_stencil_state, 0); + cso_for_each_state(sc, CSO_RASTERIZER, delete_rasterizer_state, 0); + cso_for_each_state(sc, CSO_SAMPLER, delete_sampler_state, 0); + cso_for_each_state(sc, CSO_VELEMENTS, delete_velements, 0); + + for (i = 0; i < CSO_CACHE_MAX; i++) + cso_hash_delete(sc->hashes[i]); + + FREE(sc); +} + +void cso_set_maximum_cache_size(struct cso_cache *sc, int number) +{ + int i; + + sc->max_size = number; + + for (i = 0; i < CSO_CACHE_MAX; i++) + sanitize_hash(sc, sc->hashes[i], i, sc->max_size); +} + +int cso_maximum_cache_size(const struct cso_cache *sc) +{ + return sc->max_size; +} + +void cso_cache_set_sanitize_callback(struct cso_cache *sc, + cso_sanitize_callback cb, + void *user_data) +{ + sc->sanitize_cb = cb; + sc->sanitize_data = user_data; +} + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_cache.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_cache.h new file mode 100644 index 0000000..052245f --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_cache.h @@ -0,0 +1,175 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + /** + * @file + * Constant State Object (CSO) cache. + * + * The basic idea is that the states are created via the + * create_state/bind_state/delete_state semantics. The driver is expected to + * perform as much of the Gallium state translation to whatever its internal + * representation is during the create call. Gallium then has a caching + * mechanism where it stores the created states. When the pipeline needs an + * actual state change, a bind call is issued. In the bind call the driver + * gets its already translated representation. + * + * Those semantics mean that the driver doesn't do the repeated translations + * of states on every frame, but only once, when a new state is actually + * created. + * + * Even on hardware that doesn't do any kind of state cache, it makes the + * driver look a lot neater, plus it avoids all the redundant state + * translations on every frame. + * + * Currently our constant state objects are: + * - alpha test + * - blend + * - depth stencil + * - fragment shader + * - rasterizer (old setup) + * - sampler + * - vertex shader + * - vertex elements + * + * Things that are not constant state objects include: + * - blend_color + * - clip_state + * - clear_color_state + * - constant_buffer + * - feedback_state + * - framebuffer_state + * - polygon_stipple + * - scissor_state + * - texture_state + * - viewport_state + * + * @author Zack Rusin + */ + +#ifndef CSO_CACHE_H +#define CSO_CACHE_H + +#include "pipe/p_context.h" +#include "pipe/p_state.h" + +/* cso_hash.h is necessary for cso_hash_iter, as MSVC requires structures + * returned by value to be fully defined */ +#include "cso_hash.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +enum cso_cache_type { + CSO_RASTERIZER, + CSO_BLEND, + CSO_DEPTH_STENCIL_ALPHA, + CSO_SAMPLER, + CSO_VELEMENTS, + CSO_CACHE_MAX, +}; + +typedef void (*cso_state_callback)(void *ctx, void *obj); + +typedef void (*cso_sanitize_callback)(struct cso_hash *hash, + enum cso_cache_type type, + int max_size, + void *user_data); + +struct cso_cache; + +struct cso_blend { + struct pipe_blend_state state; + void *data; + cso_state_callback delete_state; + struct pipe_context *context; +}; + +struct cso_depth_stencil_alpha { + struct pipe_depth_stencil_alpha_state state; + void *data; + cso_state_callback delete_state; + struct pipe_context *context; +}; + +struct cso_rasterizer { + struct pipe_rasterizer_state state; + void *data; + cso_state_callback delete_state; + struct pipe_context *context; +}; + +struct cso_sampler { + struct pipe_sampler_state state; + void *data; + cso_state_callback delete_state; + struct pipe_context *context; +}; + +struct cso_velems_state { + unsigned count; + struct pipe_vertex_element velems[PIPE_MAX_ATTRIBS]; +}; + +struct cso_velements { + struct cso_velems_state state; + void *data; + cso_state_callback delete_state; + struct pipe_context *context; +}; + +unsigned cso_construct_key(void *item, int item_size); + +struct cso_cache *cso_cache_create(void); +void cso_cache_delete(struct cso_cache *sc); + +void cso_cache_set_sanitize_callback(struct cso_cache *sc, + cso_sanitize_callback cb, + void *user_data); + +struct cso_hash_iter cso_insert_state(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type, + void *state); +struct cso_hash_iter cso_find_state(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type); +struct cso_hash_iter cso_find_state_template(struct cso_cache *sc, + unsigned hash_key, enum cso_cache_type type, + void *templ, unsigned size); +void cso_for_each_state(struct cso_cache *sc, enum cso_cache_type type, + cso_state_callback func, void *user_data); +void * cso_take_state(struct cso_cache *sc, unsigned hash_key, + enum cso_cache_type type); + +void cso_set_maximum_cache_size(struct cso_cache *sc, int number); +int cso_maximum_cache_size(const struct cso_cache *sc); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_hash.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_hash.c new file mode 100644 index 0000000..e04d8ed --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_hash.c @@ -0,0 +1,439 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + /* + * Authors: + * Zack Rusin + */ + +#include "util/u_debug.h" +#include "util/u_memory.h" + +#include "cso_hash.h" + +#define MAX(a, b) ((a > b) ? (a) : (b)) + +static const int MinNumBits = 4; + +static const unsigned char prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 +}; + +static int primeForNumBits(int numBits) +{ + return (1 << numBits) + prime_deltas[numBits]; +} + +/* + Returns the smallest integer n such that + primeForNumBits(n) >= hint. +*/ +static int countBits(int hint) +{ + int numBits = 0; + int bits = hint; + + while (bits > 1) { + bits >>= 1; + numBits++; + } + + if (numBits >= (int)sizeof(prime_deltas)) { + numBits = sizeof(prime_deltas) - 1; + } else if (primeForNumBits(numBits) < hint) { + ++numBits; + } + return numBits; +} + +struct cso_node { + struct cso_node *next; + unsigned key; + void *value; +}; + +struct cso_hash_data { + struct cso_node *fakeNext; + struct cso_node **buckets; + int size; + int nodeSize; + short userNumBits; + short numBits; + int numBuckets; +}; + +struct cso_hash { + union { + struct cso_hash_data *d; + struct cso_node *e; + } data; +}; + +static void *cso_data_allocate_node(struct cso_hash_data *hash) +{ + return MALLOC(hash->nodeSize); +} + +static void cso_free_node(struct cso_node *node) +{ + FREE(node); +} + +static struct cso_node * +cso_hash_create_node(struct cso_hash *hash, + unsigned akey, void *avalue, + struct cso_node **anextNode) +{ + struct cso_node *node = cso_data_allocate_node(hash->data.d); + + if (!node) + return NULL; + + node->key = akey; + node->value = avalue; + + node->next = (struct cso_node*)(*anextNode); + *anextNode = node; + ++hash->data.d->size; + return node; +} + +static void cso_data_rehash(struct cso_hash_data *hash, int hint) +{ + if (hint < 0) { + hint = countBits(-hint); + if (hint < MinNumBits) + hint = MinNumBits; + hash->userNumBits = (short)hint; + while (primeForNumBits(hint) < (hash->size >> 1)) + ++hint; + } else if (hint < MinNumBits) { + hint = MinNumBits; + } + + if (hash->numBits != hint) { + struct cso_node *e = (struct cso_node *)(hash); + struct cso_node **oldBuckets = hash->buckets; + int oldNumBuckets = hash->numBuckets; + int i = 0; + + hash->numBits = (short)hint; + hash->numBuckets = primeForNumBits(hint); + hash->buckets = MALLOC(sizeof(struct cso_node*) * hash->numBuckets); + for (i = 0; i < hash->numBuckets; ++i) + hash->buckets[i] = e; + + for (i = 0; i < oldNumBuckets; ++i) { + struct cso_node *firstNode = oldBuckets[i]; + while (firstNode != e) { + unsigned h = firstNode->key; + struct cso_node *lastNode = firstNode; + struct cso_node *afterLastNode; + struct cso_node **beforeFirstNode; + + while (lastNode->next != e && lastNode->next->key == h) + lastNode = lastNode->next; + + afterLastNode = lastNode->next; + beforeFirstNode = &hash->buckets[h % hash->numBuckets]; + while (*beforeFirstNode != e) + beforeFirstNode = &(*beforeFirstNode)->next; + lastNode->next = *beforeFirstNode; + *beforeFirstNode = firstNode; + firstNode = afterLastNode; + } + } + FREE(oldBuckets); + } +} + +static void cso_data_might_grow(struct cso_hash_data *hash) +{ + if (hash->size >= hash->numBuckets) + cso_data_rehash(hash, hash->numBits + 1); +} + +static void cso_data_has_shrunk(struct cso_hash_data *hash) +{ + if (hash->size <= (hash->numBuckets >> 3) && + hash->numBits > hash->userNumBits) { + int max = MAX(hash->numBits-2, hash->userNumBits); + cso_data_rehash(hash, max); + } +} + +static struct cso_node *cso_data_first_node(struct cso_hash_data *hash) +{ + struct cso_node *e = (struct cso_node *)(hash); + struct cso_node **bucket = hash->buckets; + int n = hash->numBuckets; + while (n--) { + if (*bucket != e) + return *bucket; + ++bucket; + } + return e; +} + +static struct cso_node **cso_hash_find_node(struct cso_hash *hash, unsigned akey) +{ + struct cso_node **node; + + if (hash->data.d->numBuckets) { + node = (struct cso_node **)(&hash->data.d->buckets[akey % hash->data.d->numBuckets]); + assert(*node == hash->data.e || (*node)->next); + while (*node != hash->data.e && (*node)->key != akey) + node = &(*node)->next; + } else { + node = (struct cso_node **)((const struct cso_node * const *)(&hash->data.e)); + } + return node; +} + +struct cso_hash_iter cso_hash_insert(struct cso_hash *hash, + unsigned key, void *data) +{ + cso_data_might_grow(hash->data.d); + + { + struct cso_node **nextNode = cso_hash_find_node(hash, key); + struct cso_node *node = cso_hash_create_node(hash, key, data, nextNode); + if (!node) { + struct cso_hash_iter null_iter = {hash, 0}; + return null_iter; + } + + { + struct cso_hash_iter iter = {hash, node}; + return iter; + } + } +} + +struct cso_hash * cso_hash_create(void) +{ + struct cso_hash *hash = MALLOC_STRUCT(cso_hash); + if (!hash) + return NULL; + + hash->data.d = MALLOC_STRUCT(cso_hash_data); + if (!hash->data.d) { + FREE(hash); + return NULL; + } + + hash->data.d->fakeNext = 0; + hash->data.d->buckets = 0; + hash->data.d->size = 0; + hash->data.d->nodeSize = sizeof(struct cso_node); + hash->data.d->userNumBits = (short)MinNumBits; + hash->data.d->numBits = 0; + hash->data.d->numBuckets = 0; + + return hash; +} + +void cso_hash_delete(struct cso_hash *hash) +{ + struct cso_node *e_for_x = (struct cso_node *)(hash->data.d); + struct cso_node **bucket = (struct cso_node **)(hash->data.d->buckets); + int n = hash->data.d->numBuckets; + while (n--) { + struct cso_node *cur = *bucket++; + while (cur != e_for_x) { + struct cso_node *next = cur->next; + cso_free_node(cur); + cur = next; + } + } + FREE(hash->data.d->buckets); + FREE(hash->data.d); + FREE(hash); +} + +struct cso_hash_iter cso_hash_find(struct cso_hash *hash, + unsigned key) +{ + struct cso_node **nextNode = cso_hash_find_node(hash, key); + struct cso_hash_iter iter = {hash, *nextNode}; + return iter; +} + +unsigned cso_hash_iter_key(struct cso_hash_iter iter) +{ + if (!iter.node || iter.hash->data.e == iter.node) + return 0; + return iter.node->key; +} + +void * cso_hash_iter_data(struct cso_hash_iter iter) +{ + if (!iter.node || iter.hash->data.e == iter.node) + return 0; + return iter.node->value; +} + +static struct cso_node *cso_hash_data_next(struct cso_node *node) +{ + union { + struct cso_node *next; + struct cso_node *e; + struct cso_hash_data *d; + } a; + int start; + struct cso_node **bucket; + int n; + + a.next = node->next; + if (!a.next) { + debug_printf("iterating beyond the last element\n"); + return 0; + } + if (a.next->next) + return a.next; + + start = (node->key % a.d->numBuckets) + 1; + bucket = a.d->buckets + start; + n = a.d->numBuckets - start; + while (n--) { + if (*bucket != a.e) + return *bucket; + ++bucket; + } + return a.e; +} + + +static struct cso_node *cso_hash_data_prev(struct cso_node *node) +{ + union { + struct cso_node *e; + struct cso_hash_data *d; + } a; + int start; + struct cso_node *sentinel; + struct cso_node **bucket; + + a.e = node; + while (a.e->next) + a.e = a.e->next; + + if (node == a.e) + start = a.d->numBuckets - 1; + else + start = node->key % a.d->numBuckets; + + sentinel = node; + bucket = a.d->buckets + start; + while (start >= 0) { + if (*bucket != sentinel) { + struct cso_node *prev = *bucket; + while (prev->next != sentinel) + prev = prev->next; + return prev; + } + + sentinel = a.e; + --bucket; + --start; + } + debug_printf("iterating backward beyond first element\n"); + return a.e; +} + +struct cso_hash_iter cso_hash_iter_next(struct cso_hash_iter iter) +{ + struct cso_hash_iter next = {iter.hash, cso_hash_data_next(iter.node)}; + return next; +} + +int cso_hash_iter_is_null(struct cso_hash_iter iter) +{ + if (!iter.node || iter.node == iter.hash->data.e) + return 1; + return 0; +} + +void * cso_hash_take(struct cso_hash *hash, + unsigned akey) +{ + struct cso_node **node = cso_hash_find_node(hash, akey); + if (*node != hash->data.e) { + void *t = (*node)->value; + struct cso_node *next = (*node)->next; + cso_free_node(*node); + *node = next; + --hash->data.d->size; + cso_data_has_shrunk(hash->data.d); + return t; + } + return 0; +} + +struct cso_hash_iter cso_hash_iter_prev(struct cso_hash_iter iter) +{ + struct cso_hash_iter prev = {iter.hash, + cso_hash_data_prev(iter.node)}; + return prev; +} + +struct cso_hash_iter cso_hash_first_node(struct cso_hash *hash) +{ + struct cso_hash_iter iter = {hash, cso_data_first_node(hash->data.d)}; + return iter; +} + +int cso_hash_size(struct cso_hash *hash) +{ + return hash->data.d->size; +} + +struct cso_hash_iter cso_hash_erase(struct cso_hash *hash, struct cso_hash_iter iter) +{ + struct cso_hash_iter ret = iter; + struct cso_node *node = iter.node; + struct cso_node **node_ptr; + + if (node == hash->data.e) + return iter; + + ret = cso_hash_iter_next(ret); + node_ptr = (struct cso_node**)(&hash->data.d->buckets[node->key % hash->data.d->numBuckets]); + while (*node_ptr != node) + node_ptr = &(*node_ptr)->next; + *node_ptr = node->next; + cso_free_node(node); + --hash->data.d->size; + return ret; +} + +boolean cso_hash_contains(struct cso_hash *hash, unsigned key) +{ + struct cso_node **node = cso_hash_find_node(hash, key); + return (*node != hash->data.e); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_hash.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_hash.h new file mode 100644 index 0000000..e58981c --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/cso_cache/cso_hash.h @@ -0,0 +1,129 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Hash table implementation. + * + * This file provides a hash implementation that is capable of dealing + * with collisions. It stores colliding entries in linked list. All + * functions operating on the hash return an iterator. The iterator + * itself points to the collision list. If there wasn't any collision + * the list will have just one entry, otherwise client code should + * iterate over the entries to find the exact entry among ones that + * had the same key (e.g. memcmp could be used on the data to check + * that) + * + * @author Zack Rusin + */ + +#ifndef CSO_HASH_H +#define CSO_HASH_H + +#include "pipe/p_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct cso_hash; +struct cso_node; + + +struct cso_hash_iter { + struct cso_hash *hash; + struct cso_node *node; +}; + + +struct cso_hash *cso_hash_create(void); +void cso_hash_delete(struct cso_hash *hash); + + +int cso_hash_size(struct cso_hash *hash); + + +/** + * Adds a data with the given key to the hash. If entry with the given + * key is already in the hash, this current entry is instered before it + * in the collision list. + * Function returns iterator pointing to the inserted item in the hash. + */ +struct cso_hash_iter cso_hash_insert(struct cso_hash *hash, unsigned key, + void *data); +/** + * Removes the item pointed to by the current iterator from the hash. + * Note that the data itself is not erased and if it was a malloc'ed pointer + * it will have to be freed after calling this function by the callee. + * Function returns iterator pointing to the item after the removed one in + * the hash. + */ +struct cso_hash_iter cso_hash_erase(struct cso_hash *hash, struct cso_hash_iter iter); + +void *cso_hash_take(struct cso_hash *hash, unsigned key); + + + +struct cso_hash_iter cso_hash_first_node(struct cso_hash *hash); + +/** + * Return an iterator pointing to the first entry in the collision list. + */ +struct cso_hash_iter cso_hash_find(struct cso_hash *hash, unsigned key); + +/** + * Returns true if a value with the given key exists in the hash + */ +boolean cso_hash_contains(struct cso_hash *hash, unsigned key); + + +int cso_hash_iter_is_null(struct cso_hash_iter iter); +unsigned cso_hash_iter_key(struct cso_hash_iter iter); +void *cso_hash_iter_data(struct cso_hash_iter iter); + + +struct cso_hash_iter cso_hash_iter_next(struct cso_hash_iter iter); +struct cso_hash_iter cso_hash_iter_prev(struct cso_hash_iter iter); + + +/** + * Convenience routine to iterate over the collision list while doing a memory + * comparison to see which entry in the list is a direct copy of our template + * and returns that entry. + */ +void *cso_hash_find_data_from_template( struct cso_hash *hash, + unsigned hash_key, + void *templ, + int size ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory.h new file mode 100644 index 0000000..46a6b6e --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory.h @@ -0,0 +1,80 @@ +/************************************************************************** + * + * Copyright 2010 Vmware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/* + * OS memory management abstractions + */ + + +#ifndef _OS_MEMORY_H_ +#define _OS_MEMORY_H_ + + +#include "pipe/p_config.h" +#include "pipe/p_compiler.h" + + +#if defined(PIPE_SUBSYSTEM_EMBEDDED) + +#ifdef __cplusplus +extern "C" { +#endif + +void * +os_malloc(size_t size); + +void * +os_calloc(size_t count, size_t size); + +void +os_free(void *ptr); + +void * +os_realloc(void *ptr, size_t old_size, size_t new_size); + +void * +os_malloc_aligned(size_t size, size_t alignment); + +void +os_free_aligned(void *ptr); + +#ifdef __cplusplus +} +#endif + +#elif defined(PIPE_OS_WINDOWS) && defined(DEBUG) && !defined(DEBUG_MEMORY_IMPLEMENTATION) + +# include "os_memory_debug.h" + +#else + +# include "os_memory_stdc.h" + +#endif + +#endif /* _OS_MEMORY_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_aligned.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_aligned.h new file mode 100644 index 0000000..90a5609 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_aligned.h @@ -0,0 +1,72 @@ +/************************************************************************** + * + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Memory alignment wrappers. + */ + + +#ifndef _OS_MEMORY_H_ +#error "Must not be included directly. Include os_memory.h instead" +#endif + + +#include "pipe/p_compiler.h" + + +/** + * Return memory on given byte alignment + */ +static inline void * +os_malloc_aligned(size_t size, size_t alignment) +{ + char *ptr, *buf; + + ptr = (char *) os_malloc(size + alignment + sizeof(void *)); + if (!ptr) + return NULL; + + buf = (char *)(((uintptr_t)ptr + sizeof(void *) + alignment - 1) & ~((uintptr_t)(alignment - 1))); + *(char **)(buf - sizeof(void *)) = ptr; + + return buf; +} + + +/** + * Free memory returned by align_malloc(). + */ +static inline void +os_free_aligned(void *ptr) +{ + if (ptr) { + void **cubbyHole = (void **) ((char *) ptr - sizeof(void *)); + void *realAddr = *cubbyHole; + os_free(realAddr); + } +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_debug.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_debug.h new file mode 100644 index 0000000..9a487de --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_debug.h @@ -0,0 +1,92 @@ +/************************************************************************** + * + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Debugging wrappers for OS memory management abstractions. + */ + + +#ifndef _OS_MEMORY_H_ +#error "Must not be included directly. Include os_memory.h instead" +#endif + + +#include "pipe/p_compiler.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +void * +debug_malloc(const char *file, unsigned line, const char *function, + size_t size); + +void * +debug_calloc(const char *file, unsigned line, const char *function, + size_t count, size_t size ); + +void +debug_free(const char *file, unsigned line, const char *function, + void *ptr); + +void * +debug_realloc(const char *file, unsigned line, const char *function, + void *old_ptr, size_t old_size, size_t new_size ); + +void +debug_memory_tag(void *ptr, unsigned tag); + +void +debug_memory_check_block(void *ptr); + +void +debug_memory_check(void); + + +#ifdef __cplusplus +} +#endif + + +#ifndef DEBUG_MEMORY_IMPLEMENTATION + +#define os_malloc( _size ) \ + debug_malloc( __FILE__, __LINE__, __FUNCTION__, _size ) +#define os_calloc( _count, _size ) \ + debug_calloc(__FILE__, __LINE__, __FUNCTION__, _count, _size ) +#define os_free( _ptr ) \ + debug_free( __FILE__, __LINE__, __FUNCTION__, _ptr ) +#define os_realloc( _ptr, _old_size, _new_size ) \ + debug_realloc( __FILE__, __LINE__, __FUNCTION__, _ptr, _old_size, _new_size ) + +/* TODO: wrap os_malloc_aligned() and os_free_aligned() too */ +#include "os_memory_aligned.h" + +#endif /* !DEBUG_MEMORY_IMPLEMENTATION */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_stdc.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_stdc.h new file mode 100644 index 0000000..c9fde06 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_memory_stdc.h @@ -0,0 +1,76 @@ +/************************************************************************** + * + * Copyright 2008-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/* + * OS memory management abstractions for the standard C library. + */ + + +#ifndef _OS_MEMORY_H_ +#error "Must not be included directly. Include os_memory.h instead" +#endif + +#include + +#include "pipe/p_compiler.h" + + +#define os_malloc(_size) malloc(_size) +#define os_calloc(_count, _size ) calloc(_count, _size ) +#define os_free(_ptr) free(_ptr) + +#define os_realloc( _old_ptr, _old_size, _new_size) \ + realloc(_old_ptr, _new_size + 0*(_old_size)) + + +#if defined(HAVE_POSIX_MEMALIGN) + +static inline void * +os_malloc_aligned(size_t size, size_t alignment) +{ + void *ptr; + alignment = (alignment + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + if(posix_memalign(&ptr, alignment, size) != 0) + return NULL; + return ptr; +} + +#define os_free_aligned(_ptr) free(_ptr) + +#elif defined(PIPE_OS_WINDOWS) + +#include + +#define os_malloc_aligned(_size, _align) _aligned_malloc(_size, _align) +#define os_free_aligned(_ptr) _aligned_free(_ptr) + +#else + +#include "os_memory_aligned.h" + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_misc.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_misc.c new file mode 100644 index 0000000..447e720 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_misc.c @@ -0,0 +1,91 @@ +/************************************************************************** + * + * Copyright 2008-2010 Vmware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#include "os_misc.h" + +#include + + +#if defined(PIPE_SUBSYSTEM_WINDOWS_USER) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif +#include +#include + +#else + +#include +#include + +#endif + + +void +os_log_message(const char *message) +{ + /* If the GALLIUM_LOG_FILE environment variable is set to a valid filename, + * write all messages to that file. + */ + static FILE *fout = NULL; + + if (!fout) { + /* one-time init */ + const char *filename = os_get_option("GALLIUM_LOG_FILE"); + if (filename) + fout = fopen(filename, "w"); + if (!fout) + fout = stderr; + } + +#if defined(PIPE_SUBSYSTEM_WINDOWS_USER) + OutputDebugStringA(message); + if(GetConsoleWindow() && !IsDebuggerPresent()) { + fflush(stdout); + fputs(message, fout); + fflush(fout); + } + else if (fout != stderr) { + fputs(message, fout); + fflush(fout); + } +#else /* !PIPE_SUBSYSTEM_WINDOWS */ + fflush(stdout); + fputs(message, fout); + fflush(fout); +#endif +} + + +const char * +os_get_option(const char *name) +{ + return getenv(name); +} + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_misc.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_misc.h new file mode 100644 index 0000000..a89256e --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_misc.h @@ -0,0 +1,106 @@ +/************************************************************************** + * + * Copyright 2010 Vmware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Miscellaneous OS services. + */ + + +#ifndef _OS_MISC_H_ +#define _OS_MISC_H_ + + +#include "pipe/p_compiler.h" + + +#if defined(PIPE_OS_UNIX) +# include /* for kill() */ +# include /* getpid() */ +#endif + +#ifdef __GNUC__ +#define UNUSED __attribute__((unused)) +#ifdef NDEBUG +#define MAYBE_UNUSED __attribute__((unused)) +#else +#define MAYBE_UNUSED +#endif +#else +#define UNUSED +#define MAYBE_UNUSED +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Trap into the debugger. + */ +#if (defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64)) && defined(PIPE_CC_GCC) +# define os_break() __asm("int3") +#elif defined(PIPE_CC_MSVC) +# define os_break() __debugbreak() +#elif defined(PIPE_OS_UNIX) +# define os_break() kill(getpid(), SIGTRAP) +#else +# define os_break() abort() +#endif + + +/* + * Abort the program. + */ +#if defined(DEBUG) +# define os_abort() os_break() +#else +# define os_abort() abort() +#endif + + +/* + * Output a message. Message should preferably end in a newline. + */ +void +os_log_message(const char *message); + + +/* + * Get an option. Should return NULL if specified option is not set. + */ +const char * +os_get_option(const char *name); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _OS_MISC_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_mman.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_mman.h new file mode 100644 index 0000000..a735309 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_mman.h @@ -0,0 +1,87 @@ +/************************************************************************** + * + * Copyright 2011 LunarG, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * OS independent memory mapping (with large file support). + * + * @author Chia-I Wu + */ + +#ifndef _OS_MMAN_H_ +#define _OS_MMAN_H_ + + +#include "pipe/p_config.h" +#include "pipe/p_compiler.h" + +#if defined(PIPE_OS_UNIX) +# ifndef _FILE_OFFSET_BITS +# error _FILE_OFFSET_BITS must be defined to 64 +# endif +# include +#else +# error Unsupported OS +#endif + +#if defined(PIPE_OS_ANDROID) +# include /* for EINVAL */ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(PIPE_OS_ANDROID) + +extern void *__mmap2(void *, size_t, int, int, int, size_t); + +static inline void *os_mmap(void *addr, size_t length, int prot, int flags, int fd, loff_t offset) +{ + /* offset must be aligned to 4096 (not necessarily the page size) */ + if (unlikely(offset & 4095)) { + errno = EINVAL; + return MAP_FAILED; + } + + return __mmap2(addr, length, prot, flags, fd, (size_t) (offset >> 12)); +} + +#else +/* assume large file support exists */ +# define os_mmap(addr, length, prot, flags, fd, offset) mmap(addr, length, prot, flags, fd, offset) +#endif + +#define os_munmap(addr, length) munmap(addr, length) + + +#ifdef __cplusplus +} +#endif + +#endif /* _OS_MMAN_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_thread.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_thread.h new file mode 100644 index 0000000..3ea0977 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/os/os_thread.h @@ -0,0 +1,292 @@ +/************************************************************************** + * + * Copyright 1999-2006 Brian Paul + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/** + * @file + * + * Thread, mutex, condition variable, barrier, semaphore and + * thread-specific data functions. + */ + + +#ifndef OS_THREAD_H_ +#define OS_THREAD_H_ + + +#include "pipe/p_compiler.h" +#include "util/u_debug.h" /* for assert */ + +#include "c11/threads.h" +#include + +/* pipe_thread + */ +typedef thrd_t pipe_thread; + +#define PIPE_THREAD_ROUTINE( name, param ) \ + int name( void *param ) + +static inline pipe_thread pipe_thread_create( PIPE_THREAD_ROUTINE((*routine), ), void *param ) +{ + pipe_thread thread; + sigset_t saved_set, new_set; + int ret; + + sigfillset(&new_set); + pthread_sigmask(SIG_SETMASK, &new_set, &saved_set); + ret = thrd_create( &thread, routine, param ); + pthread_sigmask(SIG_SETMASK, &saved_set, NULL); + + if (ret) + return 0; + + return thread; +} + +static inline int pipe_thread_wait( pipe_thread thread ) +{ + return thrd_join( thread, NULL ); +} + +static inline int pipe_thread_destroy( pipe_thread thread ) +{ + return thrd_detach( thread ); +} + + +/* pipe_mutex + */ +typedef mtx_t pipe_mutex; + +#define pipe_static_mutex(mutex) \ + static pipe_mutex mutex = _MTX_INITIALIZER_NP + +#define pipe_mutex_init(mutex) \ + (void) mtx_init(&(mutex), mtx_plain) + +#define pipe_mutex_destroy(mutex) \ + mtx_destroy(&(mutex)) + +#define pipe_mutex_lock(mutex) \ + (void) mtx_lock(&(mutex)) + +#define pipe_mutex_unlock(mutex) \ + (void) mtx_unlock(&(mutex)) + + +/* pipe_condvar + */ +typedef cnd_t pipe_condvar; + +#define pipe_condvar_init(cond) \ + cnd_init(&(cond)) + +#define pipe_condvar_destroy(cond) \ + cnd_destroy(&(cond)) + +#define pipe_condvar_wait(cond, mutex) \ + cnd_wait(&(cond), &(mutex)) + +#define pipe_condvar_signal(cond) \ + cnd_signal(&(cond)) + +#define pipe_condvar_broadcast(cond) \ + cnd_broadcast(&(cond)) + + +/* + * pipe_barrier + */ + +#if (defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) || defined(PIPE_OS_HURD)) && !defined(PIPE_OS_ANDROID) + +typedef pthread_barrier_t pipe_barrier; + +static inline void pipe_barrier_init(pipe_barrier *barrier, unsigned count) +{ + pthread_barrier_init(barrier, NULL, count); +} + +static inline void pipe_barrier_destroy(pipe_barrier *barrier) +{ + pthread_barrier_destroy(barrier); +} + +static inline void pipe_barrier_wait(pipe_barrier *barrier) +{ + pthread_barrier_wait(barrier); +} + + +#else /* If the OS doesn't have its own, implement barriers using a mutex and a condvar */ + +typedef struct { + unsigned count; + unsigned waiters; + uint64_t sequence; + pipe_mutex mutex; + pipe_condvar condvar; +} pipe_barrier; + +static inline void pipe_barrier_init(pipe_barrier *barrier, unsigned count) +{ + barrier->count = count; + barrier->waiters = 0; + barrier->sequence = 0; + pipe_mutex_init(barrier->mutex); + pipe_condvar_init(barrier->condvar); +} + +static inline void pipe_barrier_destroy(pipe_barrier *barrier) +{ + assert(barrier->waiters == 0); + pipe_mutex_destroy(barrier->mutex); + pipe_condvar_destroy(barrier->condvar); +} + +static inline void pipe_barrier_wait(pipe_barrier *barrier) +{ + pipe_mutex_lock(barrier->mutex); + + assert(barrier->waiters < barrier->count); + barrier->waiters++; + + if (barrier->waiters < barrier->count) { + uint64_t sequence = barrier->sequence; + + do { + pipe_condvar_wait(barrier->condvar, barrier->mutex); + } while (sequence == barrier->sequence); + } else { + barrier->waiters = 0; + barrier->sequence++; + pipe_condvar_broadcast(barrier->condvar); + } + + pipe_mutex_unlock(barrier->mutex); +} + + +#endif + + +/* + * Semaphores + */ + +typedef struct +{ + pipe_mutex mutex; + pipe_condvar cond; + int counter; +} pipe_semaphore; + + +static inline void +pipe_semaphore_init(pipe_semaphore *sema, int init_val) +{ + pipe_mutex_init(sema->mutex); + pipe_condvar_init(sema->cond); + sema->counter = init_val; +} + +static inline void +pipe_semaphore_destroy(pipe_semaphore *sema) +{ + pipe_mutex_destroy(sema->mutex); + pipe_condvar_destroy(sema->cond); +} + +/** Signal/increment semaphore counter */ +static inline void +pipe_semaphore_signal(pipe_semaphore *sema) +{ + pipe_mutex_lock(sema->mutex); + sema->counter++; + pipe_condvar_signal(sema->cond); + pipe_mutex_unlock(sema->mutex); +} + +/** Wait for semaphore counter to be greater than zero */ +static inline void +pipe_semaphore_wait(pipe_semaphore *sema) +{ + pipe_mutex_lock(sema->mutex); + while (sema->counter <= 0) { + pipe_condvar_wait(sema->cond, sema->mutex); + } + sema->counter--; + pipe_mutex_unlock(sema->mutex); +} + + + +/* + * Thread-specific data. + */ + +typedef struct { + tss_t key; + int initMagic; +} pipe_tsd; + + +#define PIPE_TSD_INIT_MAGIC 0xff8adc98 + + +static inline void +pipe_tsd_init(pipe_tsd *tsd) +{ + if (tss_create(&tsd->key, NULL/*free*/) != 0) { + exit(-1); + } + tsd->initMagic = PIPE_TSD_INIT_MAGIC; +} + +static inline void * +pipe_tsd_get(pipe_tsd *tsd) +{ + if (tsd->initMagic != (int) PIPE_TSD_INIT_MAGIC) { + pipe_tsd_init(tsd); + } + return tss_get(tsd->key); +} + +static inline void +pipe_tsd_set(pipe_tsd *tsd, void *value) +{ + if (tsd->initMagic != (int) PIPE_TSD_INIT_MAGIC) { + pipe_tsd_init(tsd); + } + if (tss_set(tsd->key, value) != 0) { + exit(-1); + } +} + + + +#endif /* OS_THREAD_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_build.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_build.c new file mode 100644 index 0000000..b06806d --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_build.c @@ -0,0 +1,1380 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "pipe/p_format.h" +#include "pipe/p_shader_tokens.h" +#include "tgsi_build.h" +#include "tgsi_parse.h" + + +/* + * header + */ + +struct tgsi_header +tgsi_build_header( void ) +{ + struct tgsi_header header; + + header.HeaderSize = 1; + header.BodySize = 0; + + return header; +} + +static void +header_headersize_grow( struct tgsi_header *header ) +{ + assert( header->HeaderSize < 0xFF ); + assert( header->BodySize == 0 ); + + header->HeaderSize++; +} + +static void +header_bodysize_grow( struct tgsi_header *header ) +{ + assert( header->BodySize < 0xFFFFFF ); + + header->BodySize++; +} + +struct tgsi_processor +tgsi_build_processor( + unsigned type, + struct tgsi_header *header ) +{ + struct tgsi_processor processor; + + processor.Processor = type; + processor.Padding = 0; + + header_headersize_grow( header ); + + return processor; +} + +/* + * declaration + */ + +static void +declaration_grow( + struct tgsi_declaration *declaration, + struct tgsi_header *header ) +{ + assert( declaration->NrTokens < 0xFF ); + + declaration->NrTokens++; + + header_bodysize_grow( header ); +} + +static struct tgsi_declaration +tgsi_default_declaration( void ) +{ + struct tgsi_declaration declaration; + + declaration.Type = TGSI_TOKEN_TYPE_DECLARATION; + declaration.NrTokens = 1; + declaration.File = TGSI_FILE_NULL; + declaration.UsageMask = TGSI_WRITEMASK_XYZW; + declaration.Interpolate = 0; + declaration.Dimension = 0; + declaration.Semantic = 0; + declaration.Invariant = 0; + declaration.Local = 0; + declaration.Array = 0; + declaration.Atomic = 0; + declaration.MemType = TGSI_MEMORY_TYPE_GLOBAL; + declaration.Padding = 0; + + return declaration; +} + +static struct tgsi_declaration +tgsi_build_declaration( + unsigned file, + unsigned usage_mask, + unsigned interpolate, + unsigned dimension, + unsigned semantic, + unsigned invariant, + unsigned local, + unsigned array, + unsigned atomic, + unsigned memtype, + struct tgsi_header *header ) +{ + struct tgsi_declaration declaration; + + assert( file < TGSI_FILE_COUNT ); + assert( interpolate < TGSI_INTERPOLATE_COUNT ); + + declaration = tgsi_default_declaration(); + declaration.File = file; + declaration.UsageMask = usage_mask; + declaration.Interpolate = interpolate; + declaration.Dimension = dimension; + declaration.Semantic = semantic; + declaration.Invariant = invariant; + declaration.Local = local; + declaration.Array = array; + declaration.Atomic = atomic; + declaration.MemType = memtype; + header_bodysize_grow( header ); + + return declaration; +} + +static struct tgsi_declaration_range +tgsi_default_declaration_range( void ) +{ + struct tgsi_declaration_range dr; + + dr.First = 0; + dr.Last = 0; + + return dr; +} + +static struct tgsi_declaration_range +tgsi_build_declaration_range( + unsigned first, + unsigned last, + struct tgsi_declaration *declaration, + struct tgsi_header *header ) +{ + struct tgsi_declaration_range declaration_range; + + assert( last >= first ); + assert( last <= 0xFFFF ); + + declaration_range.First = first; + declaration_range.Last = last; + + declaration_grow( declaration, header ); + + return declaration_range; +} + +static struct tgsi_declaration_dimension +tgsi_build_declaration_dimension(unsigned index_2d, + struct tgsi_declaration *declaration, + struct tgsi_header *header) +{ + struct tgsi_declaration_dimension dd; + + assert(index_2d <= 0xFFFF); + + dd.Index2D = index_2d; + dd.Padding = 0; + + declaration_grow(declaration, header); + + return dd; +} + +static struct tgsi_declaration_interp +tgsi_default_declaration_interp( void ) +{ + struct tgsi_declaration_interp di; + + di.Interpolate = TGSI_INTERPOLATE_CONSTANT; + di.Location = TGSI_INTERPOLATE_LOC_CENTER; + di.CylindricalWrap = 0; + di.Padding = 0; + + return di; +} + +static struct tgsi_declaration_interp +tgsi_build_declaration_interp(unsigned interpolate, + unsigned interpolate_location, + unsigned cylindrical_wrap, + struct tgsi_declaration *declaration, + struct tgsi_header *header) +{ + struct tgsi_declaration_interp di; + + di.Interpolate = interpolate; + di.Location = interpolate_location; + di.CylindricalWrap = cylindrical_wrap; + di.Padding = 0; + + declaration_grow(declaration, header); + + return di; +} + +static struct tgsi_declaration_semantic +tgsi_default_declaration_semantic( void ) +{ + struct tgsi_declaration_semantic ds; + + ds.Name = TGSI_SEMANTIC_POSITION; + ds.Index = 0; + ds.StreamX = 0; + ds.StreamY = 0; + ds.StreamZ = 0; + ds.StreamW = 0; + + return ds; +} + +static struct tgsi_declaration_semantic +tgsi_build_declaration_semantic( + unsigned semantic_name, + unsigned semantic_index, + unsigned streamx, + unsigned streamy, + unsigned streamz, + unsigned streamw, + struct tgsi_declaration *declaration, + struct tgsi_header *header ) +{ + struct tgsi_declaration_semantic ds; + + assert( semantic_name <= TGSI_SEMANTIC_COUNT ); + assert( semantic_index <= 0xFFFF ); + + ds.Name = semantic_name; + ds.Index = semantic_index; + ds.StreamX = streamx; + ds.StreamY = streamy; + ds.StreamZ = streamz; + ds.StreamW = streamw; + + declaration_grow( declaration, header ); + + return ds; +} + +static struct tgsi_declaration_image +tgsi_default_declaration_image(void) +{ + struct tgsi_declaration_image di; + + di.Resource = TGSI_TEXTURE_BUFFER; + di.Raw = 0; + di.Writable = 0; + di.Format = 0; + di.Padding = 0; + + return di; +} + +static struct tgsi_declaration_image +tgsi_build_declaration_image(unsigned texture, + unsigned format, + unsigned raw, + unsigned writable, + struct tgsi_declaration *declaration, + struct tgsi_header *header) +{ + struct tgsi_declaration_image di; + + di = tgsi_default_declaration_image(); + di.Resource = texture; + di.Format = format; + di.Raw = raw; + di.Writable = writable; + + declaration_grow(declaration, header); + + return di; +} + +static struct tgsi_declaration_sampler_view +tgsi_default_declaration_sampler_view(void) +{ + struct tgsi_declaration_sampler_view dsv; + + dsv.Resource = TGSI_TEXTURE_BUFFER; + dsv.ReturnTypeX = TGSI_RETURN_TYPE_UNORM; + dsv.ReturnTypeY = TGSI_RETURN_TYPE_UNORM; + dsv.ReturnTypeZ = TGSI_RETURN_TYPE_UNORM; + dsv.ReturnTypeW = TGSI_RETURN_TYPE_UNORM; + + return dsv; +} + +static struct tgsi_declaration_sampler_view +tgsi_build_declaration_sampler_view(unsigned texture, + unsigned return_type_x, + unsigned return_type_y, + unsigned return_type_z, + unsigned return_type_w, + struct tgsi_declaration *declaration, + struct tgsi_header *header) +{ + struct tgsi_declaration_sampler_view dsv; + + dsv = tgsi_default_declaration_sampler_view(); + dsv.Resource = texture; + dsv.ReturnTypeX = return_type_x; + dsv.ReturnTypeY = return_type_y; + dsv.ReturnTypeZ = return_type_z; + dsv.ReturnTypeW = return_type_w; + + declaration_grow(declaration, header); + + return dsv; +} + + +static struct tgsi_declaration_array +tgsi_default_declaration_array( void ) +{ + struct tgsi_declaration_array a; + + a.ArrayID = 0; + a.Padding = 0; + + return a; +} + +static struct tgsi_declaration_array +tgsi_build_declaration_array(unsigned arrayid, + struct tgsi_declaration *declaration, + struct tgsi_header *header) +{ + struct tgsi_declaration_array da; + + da = tgsi_default_declaration_array(); + da.ArrayID = arrayid; + + declaration_grow(declaration, header); + + return da; +} + +struct tgsi_full_declaration +tgsi_default_full_declaration( void ) +{ + struct tgsi_full_declaration full_declaration; + + full_declaration.Declaration = tgsi_default_declaration(); + full_declaration.Range = tgsi_default_declaration_range(); + full_declaration.Semantic = tgsi_default_declaration_semantic(); + full_declaration.Interp = tgsi_default_declaration_interp(); + full_declaration.Image = tgsi_default_declaration_image(); + full_declaration.SamplerView = tgsi_default_declaration_sampler_view(); + full_declaration.Array = tgsi_default_declaration_array(); + + return full_declaration; +} + +unsigned +tgsi_build_full_declaration( + const struct tgsi_full_declaration *full_decl, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ) +{ + unsigned size = 0; + struct tgsi_declaration *declaration; + struct tgsi_declaration_range *dr; + + if( maxsize <= size ) + return 0; + declaration = (struct tgsi_declaration *) &tokens[size]; + size++; + + *declaration = tgsi_build_declaration( + full_decl->Declaration.File, + full_decl->Declaration.UsageMask, + full_decl->Declaration.Interpolate, + full_decl->Declaration.Dimension, + full_decl->Declaration.Semantic, + full_decl->Declaration.Invariant, + full_decl->Declaration.Local, + full_decl->Declaration.Array, + full_decl->Declaration.Atomic, + full_decl->Declaration.MemType, + header ); + + if (maxsize <= size) + return 0; + dr = (struct tgsi_declaration_range *) &tokens[size]; + size++; + + *dr = tgsi_build_declaration_range( + full_decl->Range.First, + full_decl->Range.Last, + declaration, + header ); + + if (full_decl->Declaration.Dimension) { + struct tgsi_declaration_dimension *dd; + + if (maxsize <= size) { + return 0; + } + dd = (struct tgsi_declaration_dimension *)&tokens[size]; + size++; + + *dd = tgsi_build_declaration_dimension(full_decl->Dim.Index2D, + declaration, + header); + } + + if (full_decl->Declaration.Interpolate) { + struct tgsi_declaration_interp *di; + + if (maxsize <= size) { + return 0; + } + di = (struct tgsi_declaration_interp *)&tokens[size]; + size++; + + *di = tgsi_build_declaration_interp(full_decl->Interp.Interpolate, + full_decl->Interp.Location, + full_decl->Interp.CylindricalWrap, + declaration, + header); + } + + if( full_decl->Declaration.Semantic ) { + struct tgsi_declaration_semantic *ds; + + if( maxsize <= size ) + return 0; + ds = (struct tgsi_declaration_semantic *) &tokens[size]; + size++; + + *ds = tgsi_build_declaration_semantic( + full_decl->Semantic.Name, + full_decl->Semantic.Index, + full_decl->Semantic.StreamX, + full_decl->Semantic.StreamY, + full_decl->Semantic.StreamZ, + full_decl->Semantic.StreamW, + declaration, + header ); + } + + if (full_decl->Declaration.File == TGSI_FILE_IMAGE) { + struct tgsi_declaration_image *di; + + if (maxsize <= size) { + return 0; + } + di = (struct tgsi_declaration_image *)&tokens[size]; + size++; + + *di = tgsi_build_declaration_image(full_decl->Image.Resource, + full_decl->Image.Format, + full_decl->Image.Raw, + full_decl->Image.Writable, + declaration, + header); + } + + if (full_decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) { + struct tgsi_declaration_sampler_view *dsv; + + if (maxsize <= size) { + return 0; + } + dsv = (struct tgsi_declaration_sampler_view *)&tokens[size]; + size++; + + *dsv = tgsi_build_declaration_sampler_view( + full_decl->SamplerView.Resource, + full_decl->SamplerView.ReturnTypeX, + full_decl->SamplerView.ReturnTypeY, + full_decl->SamplerView.ReturnTypeZ, + full_decl->SamplerView.ReturnTypeW, + declaration, + header); + } + + if (full_decl->Declaration.Array) { + struct tgsi_declaration_array *da; + + if (maxsize <= size) { + return 0; + } + da = (struct tgsi_declaration_array *)&tokens[size]; + size++; + *da = tgsi_build_declaration_array( + full_decl->Array.ArrayID, + declaration, + header); + } + return size; +} + +/* + * immediate + */ + +static struct tgsi_immediate +tgsi_default_immediate( void ) +{ + struct tgsi_immediate immediate; + + immediate.Type = TGSI_TOKEN_TYPE_IMMEDIATE; + immediate.NrTokens = 1; + immediate.DataType = TGSI_IMM_FLOAT32; + immediate.Padding = 0; + + return immediate; +} + +static struct tgsi_immediate +tgsi_build_immediate( + struct tgsi_header *header, + unsigned type ) +{ + struct tgsi_immediate immediate; + + immediate = tgsi_default_immediate(); + immediate.DataType = type; + + header_bodysize_grow( header ); + + return immediate; +} + +struct tgsi_full_immediate +tgsi_default_full_immediate( void ) +{ + struct tgsi_full_immediate fullimm; + + fullimm.Immediate = tgsi_default_immediate(); + fullimm.u[0].Float = 0.0f; + fullimm.u[1].Float = 0.0f; + fullimm.u[2].Float = 0.0f; + fullimm.u[3].Float = 0.0f; + + return fullimm; +} + +static void +immediate_grow( + struct tgsi_immediate *immediate, + struct tgsi_header *header ) +{ + assert( immediate->NrTokens < 0xFF ); + + immediate->NrTokens++; + + header_bodysize_grow( header ); +} + +unsigned +tgsi_build_full_immediate( + const struct tgsi_full_immediate *full_imm, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ) +{ + unsigned size = 0; + int i; + struct tgsi_immediate *immediate; + + if( maxsize <= size ) + return 0; + immediate = (struct tgsi_immediate *) &tokens[size]; + size++; + + *immediate = tgsi_build_immediate( header, full_imm->Immediate.DataType ); + + assert( full_imm->Immediate.NrTokens <= 4 + 1 ); + + for( i = 0; i < full_imm->Immediate.NrTokens - 1; i++ ) { + union tgsi_immediate_data *data; + + if( maxsize <= size ) + return 0; + + data = (union tgsi_immediate_data *) &tokens[size]; + *data = full_imm->u[i]; + + immediate_grow( immediate, header ); + size++; + } + + return size; +} + +/* + * instruction + */ + +struct tgsi_instruction +tgsi_default_instruction( void ) +{ + struct tgsi_instruction instruction; + + instruction.Type = TGSI_TOKEN_TYPE_INSTRUCTION; + instruction.NrTokens = 0; + instruction.Opcode = TGSI_OPCODE_MOV; + instruction.Saturate = 0; + instruction.NumDstRegs = 1; + instruction.NumSrcRegs = 1; + instruction.Label = 0; + instruction.Texture = 0; + instruction.Memory = 0; + instruction.Precise = 0; + + return instruction; +} + +static struct tgsi_instruction +tgsi_build_instruction(unsigned opcode, + unsigned saturate, + unsigned precise, + unsigned num_dst_regs, + unsigned num_src_regs, + struct tgsi_header *header) +{ + struct tgsi_instruction instruction; + + assert (opcode <= TGSI_OPCODE_LAST); + assert (saturate <= 1); + assert (num_dst_regs <= 3); + assert (num_src_regs <= 15); + + instruction = tgsi_default_instruction(); + instruction.Opcode = opcode; + instruction.Saturate = saturate; + instruction.Precise = precise; + instruction.NumDstRegs = num_dst_regs; + instruction.NumSrcRegs = num_src_regs; + + header_bodysize_grow( header ); + + return instruction; +} + +static void +instruction_grow( + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + assert (instruction->NrTokens < 0xFF); + + instruction->NrTokens++; + + header_bodysize_grow( header ); +} + +static struct tgsi_instruction_label +tgsi_default_instruction_label( void ) +{ + struct tgsi_instruction_label instruction_label; + + instruction_label.Label = 0; + instruction_label.Padding = 0; + + return instruction_label; +} + +static struct tgsi_instruction_label +tgsi_build_instruction_label( + unsigned label, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_instruction_label instruction_label; + + instruction_label.Label = label; + instruction_label.Padding = 0; + instruction->Label = 1; + + instruction_grow( instruction, header ); + + return instruction_label; +} + +static struct tgsi_instruction_texture +tgsi_default_instruction_texture( void ) +{ + struct tgsi_instruction_texture instruction_texture; + + instruction_texture.Texture = TGSI_TEXTURE_UNKNOWN; + instruction_texture.NumOffsets = 0; + instruction_texture.Padding = 0; + + return instruction_texture; +} + +static struct tgsi_instruction_texture +tgsi_build_instruction_texture( + unsigned texture, + unsigned num_offsets, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_instruction_texture instruction_texture; + + instruction_texture.Texture = texture; + instruction_texture.NumOffsets = num_offsets; + instruction_texture.Padding = 0; + instruction->Texture = 1; + + instruction_grow( instruction, header ); + + return instruction_texture; +} + +static struct tgsi_instruction_memory +tgsi_default_instruction_memory( void ) +{ + struct tgsi_instruction_memory instruction_memory; + + instruction_memory.Qualifier = 0; + instruction_memory.Texture = 0; + instruction_memory.Format = 0; + instruction_memory.Padding = 0; + + return instruction_memory; +} + +static struct tgsi_instruction_memory +tgsi_build_instruction_memory( + unsigned qualifier, + unsigned texture, + unsigned format, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_instruction_memory instruction_memory; + + instruction_memory.Qualifier = qualifier; + instruction_memory.Texture = texture; + instruction_memory.Format = format; + instruction_memory.Padding = 0; + instruction->Memory = 1; + + instruction_grow( instruction, header ); + + return instruction_memory; +} + +static struct tgsi_texture_offset +tgsi_default_texture_offset( void ) +{ + struct tgsi_texture_offset texture_offset; + + texture_offset.Index = 0; + texture_offset.File = 0; + texture_offset.SwizzleX = 0; + texture_offset.SwizzleY = 0; + texture_offset.SwizzleZ = 0; + texture_offset.Padding = 0; + + return texture_offset; +} + +static struct tgsi_texture_offset +tgsi_build_texture_offset( + int index, int file, int swizzle_x, int swizzle_y, int swizzle_z, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_texture_offset texture_offset; + + texture_offset.Index = index; + texture_offset.File = file; + texture_offset.SwizzleX = swizzle_x; + texture_offset.SwizzleY = swizzle_y; + texture_offset.SwizzleZ = swizzle_z; + texture_offset.Padding = 0; + + instruction_grow( instruction, header ); + + return texture_offset; +} + +static struct tgsi_src_register +tgsi_default_src_register( void ) +{ + struct tgsi_src_register src_register; + + src_register.File = TGSI_FILE_NULL; + src_register.SwizzleX = TGSI_SWIZZLE_X; + src_register.SwizzleY = TGSI_SWIZZLE_Y; + src_register.SwizzleZ = TGSI_SWIZZLE_Z; + src_register.SwizzleW = TGSI_SWIZZLE_W; + src_register.Negate = 0; + src_register.Absolute = 0; + src_register.Indirect = 0; + src_register.Dimension = 0; + src_register.Index = 0; + + return src_register; +} + +static struct tgsi_src_register +tgsi_build_src_register( + unsigned file, + unsigned swizzle_x, + unsigned swizzle_y, + unsigned swizzle_z, + unsigned swizzle_w, + unsigned negate, + unsigned absolute, + unsigned indirect, + unsigned dimension, + int index, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_src_register src_register; + + assert( file < TGSI_FILE_COUNT ); + assert( swizzle_x <= TGSI_SWIZZLE_W ); + assert( swizzle_y <= TGSI_SWIZZLE_W ); + assert( swizzle_z <= TGSI_SWIZZLE_W ); + assert( swizzle_w <= TGSI_SWIZZLE_W ); + assert( negate <= 1 ); + assert( index >= -0x8000 && index <= 0x7FFF ); + + src_register.File = file; + src_register.SwizzleX = swizzle_x; + src_register.SwizzleY = swizzle_y; + src_register.SwizzleZ = swizzle_z; + src_register.SwizzleW = swizzle_w; + src_register.Negate = negate; + src_register.Absolute = absolute; + src_register.Indirect = indirect; + src_register.Dimension = dimension; + src_register.Index = index; + + instruction_grow( instruction, header ); + + return src_register; +} + +static struct tgsi_ind_register +tgsi_default_ind_register( void ) +{ + struct tgsi_ind_register ind_register; + + ind_register.File = TGSI_FILE_NULL; + ind_register.Index = 0; + ind_register.Swizzle = TGSI_SWIZZLE_X; + ind_register.ArrayID = 0; + + return ind_register; +} + +static struct tgsi_ind_register +tgsi_build_ind_register( + unsigned file, + unsigned swizzle, + int index, + unsigned arrayid, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_ind_register ind_register; + + assert( file < TGSI_FILE_COUNT ); + assert( swizzle <= TGSI_SWIZZLE_W ); + assert( index >= -0x8000 && index <= 0x7FFF ); + + ind_register.File = file; + ind_register.Swizzle = swizzle; + ind_register.Index = index; + ind_register.ArrayID = arrayid; + + instruction_grow( instruction, header ); + + return ind_register; +} + +static struct tgsi_dimension +tgsi_default_dimension( void ) +{ + struct tgsi_dimension dimension; + + dimension.Indirect = 0; + dimension.Dimension = 0; + dimension.Padding = 0; + dimension.Index = 0; + + return dimension; +} + +static struct tgsi_full_src_register +tgsi_default_full_src_register( void ) +{ + struct tgsi_full_src_register full_src_register; + + full_src_register.Register = tgsi_default_src_register(); + full_src_register.Indirect = tgsi_default_ind_register(); + full_src_register.Dimension = tgsi_default_dimension(); + full_src_register.DimIndirect = tgsi_default_ind_register(); + + return full_src_register; +} + +static struct tgsi_dimension +tgsi_build_dimension( + unsigned indirect, + unsigned index, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_dimension dimension; + + dimension.Indirect = indirect; + dimension.Dimension = 0; + dimension.Padding = 0; + dimension.Index = index; + + instruction_grow( instruction, header ); + + return dimension; +} + +static struct tgsi_dst_register +tgsi_default_dst_register( void ) +{ + struct tgsi_dst_register dst_register; + + dst_register.File = TGSI_FILE_NULL; + dst_register.WriteMask = TGSI_WRITEMASK_XYZW; + dst_register.Indirect = 0; + dst_register.Dimension = 0; + dst_register.Index = 0; + dst_register.Padding = 0; + + return dst_register; +} + +static struct tgsi_dst_register +tgsi_build_dst_register( + unsigned file, + unsigned mask, + unsigned indirect, + unsigned dimension, + int index, + struct tgsi_instruction *instruction, + struct tgsi_header *header ) +{ + struct tgsi_dst_register dst_register; + + assert( file < TGSI_FILE_COUNT ); + assert( mask <= TGSI_WRITEMASK_XYZW ); + assert( index >= -32768 && index <= 32767 ); + + dst_register.File = file; + dst_register.WriteMask = mask; + dst_register.Indirect = indirect; + dst_register.Dimension = dimension; + dst_register.Index = index; + dst_register.Padding = 0; + + instruction_grow( instruction, header ); + + return dst_register; +} + +static struct tgsi_full_dst_register +tgsi_default_full_dst_register( void ) +{ + struct tgsi_full_dst_register full_dst_register; + + full_dst_register.Register = tgsi_default_dst_register(); + full_dst_register.Indirect = tgsi_default_ind_register(); + full_dst_register.Dimension = tgsi_default_dimension(); + full_dst_register.DimIndirect = tgsi_default_ind_register(); + + return full_dst_register; +} + +struct tgsi_full_instruction +tgsi_default_full_instruction( void ) +{ + struct tgsi_full_instruction full_instruction; + unsigned i; + + full_instruction.Instruction = tgsi_default_instruction(); + full_instruction.Label = tgsi_default_instruction_label(); + full_instruction.Texture = tgsi_default_instruction_texture(); + full_instruction.Memory = tgsi_default_instruction_memory(); + for( i = 0; i < TGSI_FULL_MAX_TEX_OFFSETS; i++ ) { + full_instruction.TexOffsets[i] = tgsi_default_texture_offset(); + } + for( i = 0; i < TGSI_FULL_MAX_DST_REGISTERS; i++ ) { + full_instruction.Dst[i] = tgsi_default_full_dst_register(); + } + for( i = 0; i < TGSI_FULL_MAX_SRC_REGISTERS; i++ ) { + full_instruction.Src[i] = tgsi_default_full_src_register(); + } + + return full_instruction; +} + +unsigned +tgsi_build_full_instruction( + const struct tgsi_full_instruction *full_inst, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ) +{ + unsigned size = 0; + unsigned i; + struct tgsi_instruction *instruction; + + if( maxsize <= size ) + return 0; + instruction = (struct tgsi_instruction *) &tokens[size]; + size++; + + *instruction = tgsi_build_instruction(full_inst->Instruction.Opcode, + full_inst->Instruction.Saturate, + full_inst->Instruction.Precise, + full_inst->Instruction.NumDstRegs, + full_inst->Instruction.NumSrcRegs, + header); + + if (full_inst->Instruction.Label) { + struct tgsi_instruction_label *instruction_label; + + if( maxsize <= size ) + return 0; + instruction_label = + (struct tgsi_instruction_label *) &tokens[size]; + size++; + + *instruction_label = tgsi_build_instruction_label( + full_inst->Label.Label, + instruction, + header ); + } + + if (full_inst->Instruction.Texture) { + struct tgsi_instruction_texture *instruction_texture; + + if( maxsize <= size ) + return 0; + instruction_texture = + (struct tgsi_instruction_texture *) &tokens[size]; + size++; + + *instruction_texture = tgsi_build_instruction_texture( + full_inst->Texture.Texture, + full_inst->Texture.NumOffsets, + instruction, + header ); + + for (i = 0; i < full_inst->Texture.NumOffsets; i++) { + struct tgsi_texture_offset *texture_offset; + + if ( maxsize <= size ) + return 0; + texture_offset = (struct tgsi_texture_offset *)&tokens[size]; + size++; + *texture_offset = tgsi_build_texture_offset( + full_inst->TexOffsets[i].Index, + full_inst->TexOffsets[i].File, + full_inst->TexOffsets[i].SwizzleX, + full_inst->TexOffsets[i].SwizzleY, + full_inst->TexOffsets[i].SwizzleZ, + instruction, + header); + } + } + + if (full_inst->Instruction.Memory) { + struct tgsi_instruction_memory *instruction_memory; + + if( maxsize <= size ) + return 0; + instruction_memory = + (struct tgsi_instruction_memory *) &tokens[size]; + size++; + + *instruction_memory = tgsi_build_instruction_memory( + full_inst->Memory.Qualifier, + full_inst->Memory.Texture, + full_inst->Memory.Format, + instruction, + header ); + } + + for( i = 0; i < full_inst->Instruction.NumDstRegs; i++ ) { + const struct tgsi_full_dst_register *reg = &full_inst->Dst[i]; + struct tgsi_dst_register *dst_register; + + if( maxsize <= size ) + return 0; + dst_register = (struct tgsi_dst_register *) &tokens[size]; + size++; + + *dst_register = tgsi_build_dst_register( + reg->Register.File, + reg->Register.WriteMask, + reg->Register.Indirect, + reg->Register.Dimension, + reg->Register.Index, + instruction, + header ); + + if( reg->Register.Indirect ) { + struct tgsi_ind_register *ind; + + if( maxsize <= size ) + return 0; + ind = (struct tgsi_ind_register *) &tokens[size]; + size++; + + *ind = tgsi_build_ind_register( + reg->Indirect.File, + reg->Indirect.Swizzle, + reg->Indirect.Index, + reg->Indirect.ArrayID, + instruction, + header ); + } + + if( reg->Register.Dimension ) { + struct tgsi_dimension *dim; + + assert( !reg->Dimension.Dimension ); + + if( maxsize <= size ) + return 0; + dim = (struct tgsi_dimension *) &tokens[size]; + size++; + + *dim = tgsi_build_dimension( + reg->Dimension.Indirect, + reg->Dimension.Index, + instruction, + header ); + + if( reg->Dimension.Indirect ) { + struct tgsi_ind_register *ind; + + if( maxsize <= size ) + return 0; + ind = (struct tgsi_ind_register *) &tokens[size]; + size++; + + *ind = tgsi_build_ind_register( + reg->DimIndirect.File, + reg->DimIndirect.Swizzle, + reg->DimIndirect.Index, + reg->DimIndirect.ArrayID, + instruction, + header ); + } + } + } + + for( i = 0; i < full_inst->Instruction.NumSrcRegs; i++ ) { + const struct tgsi_full_src_register *reg = &full_inst->Src[i]; + struct tgsi_src_register *src_register; + + if( maxsize <= size ) + return 0; + src_register = (struct tgsi_src_register *) &tokens[size]; + size++; + + *src_register = tgsi_build_src_register( + reg->Register.File, + reg->Register.SwizzleX, + reg->Register.SwizzleY, + reg->Register.SwizzleZ, + reg->Register.SwizzleW, + reg->Register.Negate, + reg->Register.Absolute, + reg->Register.Indirect, + reg->Register.Dimension, + reg->Register.Index, + instruction, + header ); + + if( reg->Register.Indirect ) { + struct tgsi_ind_register *ind; + + if( maxsize <= size ) + return 0; + ind = (struct tgsi_ind_register *) &tokens[size]; + size++; + + *ind = tgsi_build_ind_register( + reg->Indirect.File, + reg->Indirect.Swizzle, + reg->Indirect.Index, + reg->Indirect.ArrayID, + instruction, + header ); + } + + if( reg->Register.Dimension ) { + struct tgsi_dimension *dim; + + assert( !reg->Dimension.Dimension ); + + if( maxsize <= size ) + return 0; + dim = (struct tgsi_dimension *) &tokens[size]; + size++; + + *dim = tgsi_build_dimension( + reg->Dimension.Indirect, + reg->Dimension.Index, + instruction, + header ); + + if( reg->Dimension.Indirect ) { + struct tgsi_ind_register *ind; + + if( maxsize <= size ) + return 0; + ind = (struct tgsi_ind_register *) &tokens[size]; + size++; + + *ind = tgsi_build_ind_register( + reg->DimIndirect.File, + reg->DimIndirect.Swizzle, + reg->DimIndirect.Index, + reg->DimIndirect.ArrayID, + instruction, + header ); + } + } + } + + return size; +} + +static struct tgsi_property +tgsi_default_property( void ) +{ + struct tgsi_property property; + + property.Type = TGSI_TOKEN_TYPE_PROPERTY; + property.NrTokens = 1; + property.PropertyName = TGSI_PROPERTY_GS_INPUT_PRIM; + property.Padding = 0; + + return property; +} + +static struct tgsi_property +tgsi_build_property(unsigned property_name, + struct tgsi_header *header) +{ + struct tgsi_property property; + + property = tgsi_default_property(); + property.PropertyName = property_name; + + header_bodysize_grow( header ); + + return property; +} + + +struct tgsi_full_property +tgsi_default_full_property( void ) +{ + struct tgsi_full_property full_property; + + full_property.Property = tgsi_default_property(); + memset(full_property.u, 0, + sizeof(struct tgsi_property_data) * 8); + + return full_property; +} + +static void +property_grow( + struct tgsi_property *property, + struct tgsi_header *header ) +{ + assert( property->NrTokens < 0xFF ); + + property->NrTokens++; + + header_bodysize_grow( header ); +} + +static struct tgsi_property_data +tgsi_build_property_data( + unsigned value, + struct tgsi_property *property, + struct tgsi_header *header ) +{ + struct tgsi_property_data property_data; + + property_data.Data = value; + + property_grow( property, header ); + + return property_data; +} + +unsigned +tgsi_build_full_property( + const struct tgsi_full_property *full_prop, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ) +{ + unsigned size = 0; + int i; + struct tgsi_property *property; + + if( maxsize <= size ) + return 0; + property = (struct tgsi_property *) &tokens[size]; + size++; + + *property = tgsi_build_property( + full_prop->Property.PropertyName, + header ); + + assert( full_prop->Property.NrTokens <= 8 + 1 ); + + for( i = 0; i < full_prop->Property.NrTokens - 1; i++ ) { + struct tgsi_property_data *data; + + if( maxsize <= size ) + return 0; + data = (struct tgsi_property_data *) &tokens[size]; + size++; + + *data = tgsi_build_property_data( + full_prop->u[i].Data, + property, + header ); + } + + return size; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_build.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_build.h new file mode 100644 index 0000000..c5127e1 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_build.h @@ -0,0 +1,118 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_BUILD_H +#define TGSI_BUILD_H + + +struct tgsi_token; + + +#if defined __cplusplus +extern "C" { +#endif + + +/* + * header + */ + +struct tgsi_header +tgsi_build_header( void ); + +struct tgsi_processor +tgsi_build_processor( + unsigned processor, + struct tgsi_header *header ); + +/* + * declaration + */ + +struct tgsi_full_declaration +tgsi_default_full_declaration( void ); + +unsigned +tgsi_build_full_declaration( + const struct tgsi_full_declaration *full_decl, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ); + +/* + * immediate + */ + +struct tgsi_full_immediate +tgsi_default_full_immediate( void ); + +unsigned +tgsi_build_full_immediate( + const struct tgsi_full_immediate *full_imm, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ); + +/* + * properties + */ + +struct tgsi_full_property +tgsi_default_full_property( void ); + +unsigned +tgsi_build_full_property( + const struct tgsi_full_property *full_prop, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ); + +/* + * instruction + */ + +struct tgsi_instruction +tgsi_default_instruction( void ); + +struct tgsi_full_instruction +tgsi_default_full_instruction( void ); + +unsigned +tgsi_build_full_instruction( + const struct tgsi_full_instruction *full_inst, + struct tgsi_token *tokens, + struct tgsi_header *header, + unsigned maxsize ); + +struct tgsi_instruction_predicate +tgsi_default_instruction_predicate(void); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_BUILD_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_dump.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_dump.c new file mode 100644 index 0000000..64cd397 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_dump.c @@ -0,0 +1,814 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "util/u_string.h" +#include "util/u_math.h" +#include "util/u_memory.h" +#include "util/u_math.h" +#include "tgsi_dump.h" +#include "tgsi_info.h" +#include "tgsi_iterate.h" +#include "tgsi_strings.h" + + +/** Number of spaces to indent for IF/LOOP/etc */ +static const int indent_spaces = 3; + + +struct dump_ctx +{ + struct tgsi_iterate_context iter; + + boolean dump_float_as_hex; + + uint instno; + uint immno; + int indent; + + uint indentation; + FILE *file; + + void (*dump_printf)(struct dump_ctx *ctx, const char *format, ...); +}; + +static void +dump_ctx_printf(struct dump_ctx *ctx, const char *format, ...) +{ + va_list ap; + (void)ctx; + va_start(ap, format); + if (ctx->file) + vfprintf(ctx->file, format, ap); + else + _debug_vprintf(format, ap); + va_end(ap); +} + +static void +dump_enum( + struct dump_ctx *ctx, + uint e, + const char **enums, + uint enum_count ) +{ + if (e >= enum_count) + ctx->dump_printf( ctx, "%u", e ); + else + ctx->dump_printf( ctx, "%s", enums[e] ); +} + +#define EOL() ctx->dump_printf( ctx, "\n" ) +#define TXT(S) ctx->dump_printf( ctx, "%s", S ) +#define CHR(C) ctx->dump_printf( ctx, "%c", C ) +#define UIX(I) ctx->dump_printf( ctx, "0x%x", I ) +#define UID(I) ctx->dump_printf( ctx, "%u", I ) +#define INSTID(I) ctx->dump_printf( ctx, "% 3u", I ) +#define SID(I) ctx->dump_printf( ctx, "%d", I ) +#define FLT(F) ctx->dump_printf( ctx, "%10.4f", F ) +#define DBL(D) ctx->dump_printf( ctx, "%10.8f", D ) +#define HFLT(F) ctx->dump_printf( ctx, "0x%08x", fui((F)) ) +#define ENM(E,ENUMS) dump_enum( ctx, E, ENUMS, sizeof( ENUMS ) / sizeof( *ENUMS ) ) + +const char * +tgsi_swizzle_names[4] = +{ + "x", + "y", + "z", + "w" +}; + +static void +_dump_register_src( + struct dump_ctx *ctx, + const struct tgsi_full_src_register *src ) +{ + TXT(tgsi_file_name(src->Register.File)); + if (src->Register.Dimension) { + if (src->Dimension.Indirect) { + CHR( '[' ); + TXT(tgsi_file_name(src->DimIndirect.File)); + CHR( '[' ); + SID( src->DimIndirect.Index ); + TXT( "]." ); + ENM( src->DimIndirect.Swizzle, tgsi_swizzle_names ); + if (src->Dimension.Index != 0) { + if (src->Dimension.Index > 0) + CHR( '+' ); + SID( src->Dimension.Index ); + } + CHR( ']' ); + if (src->DimIndirect.ArrayID) { + CHR( '(' ); + SID( src->DimIndirect.ArrayID ); + CHR( ')' ); + } + } else { + CHR('['); + SID(src->Dimension.Index); + CHR(']'); + } + } + if (src->Register.Indirect) { + CHR( '[' ); + TXT(tgsi_file_name(src->Indirect.File)); + CHR( '[' ); + SID( src->Indirect.Index ); + TXT( "]." ); + ENM( src->Indirect.Swizzle, tgsi_swizzle_names ); + if (src->Register.Index != 0) { + if (src->Register.Index > 0) + CHR( '+' ); + SID( src->Register.Index ); + } + CHR( ']' ); + if (src->Indirect.ArrayID) { + CHR( '(' ); + SID( src->Indirect.ArrayID ); + CHR( ')' ); + } + } else { + CHR( '[' ); + SID( src->Register.Index ); + CHR( ']' ); + } +} + + +static void +_dump_register_dst( + struct dump_ctx *ctx, + const struct tgsi_full_dst_register *dst ) +{ + TXT(tgsi_file_name(dst->Register.File)); + if (dst->Register.Dimension) { + if (dst->Dimension.Indirect) { + CHR( '[' ); + TXT(tgsi_file_name(dst->DimIndirect.File)); + CHR( '[' ); + SID( dst->DimIndirect.Index ); + TXT( "]." ); + ENM( dst->DimIndirect.Swizzle, tgsi_swizzle_names ); + if (dst->Dimension.Index != 0) { + if (dst->Dimension.Index > 0) + CHR( '+' ); + SID( dst->Dimension.Index ); + } + CHR( ']' ); + if (dst->DimIndirect.ArrayID) { + CHR( '(' ); + SID( dst->DimIndirect.ArrayID ); + CHR( ')' ); + } + } else { + CHR('['); + SID(dst->Dimension.Index); + CHR(']'); + } + } + if (dst->Register.Indirect) { + CHR( '[' ); + TXT(tgsi_file_name(dst->Indirect.File)); + CHR( '[' ); + SID( dst->Indirect.Index ); + TXT( "]." ); + ENM( dst->Indirect.Swizzle, tgsi_swizzle_names ); + if (dst->Register.Index != 0) { + if (dst->Register.Index > 0) + CHR( '+' ); + SID( dst->Register.Index ); + } + CHR( ']' ); + if (dst->Indirect.ArrayID) { + CHR( '(' ); + SID( dst->Indirect.ArrayID ); + CHR( ')' ); + } + } else { + CHR( '[' ); + SID( dst->Register.Index ); + CHR( ']' ); + } +} +static void +_dump_writemask( + struct dump_ctx *ctx, + uint writemask ) +{ + if (writemask != TGSI_WRITEMASK_XYZW) { + CHR( '.' ); + if (writemask & TGSI_WRITEMASK_X) + CHR( 'x' ); + if (writemask & TGSI_WRITEMASK_Y) + CHR( 'y' ); + if (writemask & TGSI_WRITEMASK_Z) + CHR( 'z' ); + if (writemask & TGSI_WRITEMASK_W) + CHR( 'w' ); + } +} + +static void +dump_imm_data(struct tgsi_iterate_context *iter, + union tgsi_immediate_data *data, + unsigned num_tokens, + unsigned data_type) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + unsigned i ; + + TXT( " {" ); + + assert( num_tokens <= 4 ); + for (i = 0; i < num_tokens; i++) { + switch (data_type) { + case TGSI_IMM_FLOAT64: { + union di d; + d.ui = data[i].Uint | (uint64_t)data[i+1].Uint << 32; + DBL( d.d ); + i++; + break; + } + case TGSI_IMM_FLOAT32: + if (ctx->dump_float_as_hex) + HFLT( data[i].Float ); + else + FLT( data[i].Float ); + break; + case TGSI_IMM_UINT32: + UID(data[i].Uint); + break; + case TGSI_IMM_INT32: + SID(data[i].Int); + break; + default: + assert( 0 ); + } + + if (i < num_tokens - 1) + TXT( ", " ); + } + TXT( "}" ); +} + +static boolean +iter_declaration( + struct tgsi_iterate_context *iter, + struct tgsi_full_declaration *decl ) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + boolean patch = decl->Semantic.Name == TGSI_SEMANTIC_PATCH || + decl->Semantic.Name == TGSI_SEMANTIC_TESSINNER || + decl->Semantic.Name == TGSI_SEMANTIC_TESSOUTER || + decl->Semantic.Name == TGSI_SEMANTIC_PRIMID; + + TXT( "DCL " ); + + TXT(tgsi_file_name(decl->Declaration.File)); + + /* all geometry shader inputs and non-patch tessellation shader inputs are + * two dimensional + */ + if (decl->Declaration.File == TGSI_FILE_INPUT && + (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY || + (!patch && + (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL || + iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL)))) { + TXT("[]"); + } + + /* all non-patch tess ctrl shader outputs are two dimensional */ + if (decl->Declaration.File == TGSI_FILE_OUTPUT && + !patch && + iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) { + TXT("[]"); + } + + if (decl->Declaration.Dimension) { + CHR('['); + SID(decl->Dim.Index2D); + CHR(']'); + } + + CHR('['); + SID(decl->Range.First); + if (decl->Range.First != decl->Range.Last) { + TXT(".."); + SID(decl->Range.Last); + } + CHR(']'); + + _dump_writemask( + ctx, + decl->Declaration.UsageMask ); + + if (decl->Declaration.Array) { + TXT( ", ARRAY(" ); + SID(decl->Array.ArrayID); + CHR(')'); + } + + if (decl->Declaration.Local) + TXT( ", LOCAL" ); + + if (decl->Declaration.Semantic) { + TXT( ", " ); + ENM( decl->Semantic.Name, tgsi_semantic_names ); + if (decl->Semantic.Index != 0 || + decl->Semantic.Name == TGSI_SEMANTIC_TEXCOORD || + decl->Semantic.Name == TGSI_SEMANTIC_GENERIC) { + CHR( '[' ); + UID( decl->Semantic.Index ); + CHR( ']' ); + } + } + + if (decl->Declaration.File == TGSI_FILE_IMAGE) { + TXT(", "); + ENM(decl->Image.Resource, tgsi_texture_names); + TXT(", "); + TXT(util_format_name(decl->Image.Format)); + if (decl->Image.Writable) + TXT(", WR"); + if (decl->Image.Raw) + TXT(", RAW"); + } + + if (decl->Declaration.File == TGSI_FILE_BUFFER) { + if (decl->Declaration.Atomic) + TXT(", ATOMIC"); + } + + if (decl->Declaration.File == TGSI_FILE_MEMORY) { + switch (decl->Declaration.MemType) { + /* Note: ,GLOBAL is optional / the default */ + case TGSI_MEMORY_TYPE_GLOBAL: TXT(", GLOBAL"); break; + case TGSI_MEMORY_TYPE_SHARED: TXT(", SHARED"); break; + case TGSI_MEMORY_TYPE_PRIVATE: TXT(", PRIVATE"); break; + case TGSI_MEMORY_TYPE_INPUT: TXT(", INPUT"); break; + } + } + + if (decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) { + TXT(", "); + ENM(decl->SamplerView.Resource, tgsi_texture_names); + TXT(", "); + if ((decl->SamplerView.ReturnTypeX == decl->SamplerView.ReturnTypeY) && + (decl->SamplerView.ReturnTypeX == decl->SamplerView.ReturnTypeZ) && + (decl->SamplerView.ReturnTypeX == decl->SamplerView.ReturnTypeW)) { + ENM(decl->SamplerView.ReturnTypeX, tgsi_return_type_names); + } else { + ENM(decl->SamplerView.ReturnTypeX, tgsi_return_type_names); + TXT(", "); + ENM(decl->SamplerView.ReturnTypeY, tgsi_return_type_names); + TXT(", "); + ENM(decl->SamplerView.ReturnTypeZ, tgsi_return_type_names); + TXT(", "); + ENM(decl->SamplerView.ReturnTypeW, tgsi_return_type_names); + } + } + + if (decl->Declaration.Interpolate) { + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT && + decl->Declaration.File == TGSI_FILE_INPUT) + { + TXT( ", " ); + ENM( decl->Interp.Interpolate, tgsi_interpolate_names ); + } + + if (decl->Interp.Location != TGSI_INTERPOLATE_LOC_CENTER) { + TXT( ", " ); + ENM( decl->Interp.Location, tgsi_interpolate_locations ); + } + + if (decl->Interp.CylindricalWrap) { + TXT(", CYLWRAP_"); + if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_X) { + CHR('X'); + } + if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_Y) { + CHR('Y'); + } + if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_Z) { + CHR('Z'); + } + if (decl->Interp.CylindricalWrap & TGSI_CYLINDRICAL_WRAP_W) { + CHR('W'); + } + } + } + + if (decl->Declaration.Invariant) { + TXT( ", INVARIANT" ); + } + + EOL(); + + return TRUE; +} + +void +tgsi_dump_declaration( + const struct tgsi_full_declaration *decl ) +{ + struct dump_ctx ctx; + + ctx.dump_printf = dump_ctx_printf; + + iter_declaration( &ctx.iter, (struct tgsi_full_declaration *)decl ); +} + +static boolean +iter_property( + struct tgsi_iterate_context *iter, + struct tgsi_full_property *prop ) +{ + int i; + struct dump_ctx *ctx = (struct dump_ctx *)iter; + + TXT( "PROPERTY " ); + ENM(prop->Property.PropertyName, tgsi_property_names); + + if (prop->Property.NrTokens > 1) + TXT(" "); + + for (i = 0; i < prop->Property.NrTokens - 1; ++i) { + switch (prop->Property.PropertyName) { + case TGSI_PROPERTY_GS_INPUT_PRIM: + case TGSI_PROPERTY_GS_OUTPUT_PRIM: + ENM(prop->u[i].Data, tgsi_primitive_names); + break; + case TGSI_PROPERTY_FS_COORD_ORIGIN: + ENM(prop->u[i].Data, tgsi_fs_coord_origin_names); + break; + case TGSI_PROPERTY_FS_COORD_PIXEL_CENTER: + ENM(prop->u[i].Data, tgsi_fs_coord_pixel_center_names); + break; + default: + SID( prop->u[i].Data ); + break; + } + if (i < prop->Property.NrTokens - 2) + TXT( ", " ); + } + EOL(); + + return TRUE; +} + +void tgsi_dump_property( + const struct tgsi_full_property *prop ) +{ + struct dump_ctx ctx; + + ctx.dump_printf = dump_ctx_printf; + + iter_property( &ctx.iter, (struct tgsi_full_property *)prop ); +} + +static boolean +iter_immediate( + struct tgsi_iterate_context *iter, + struct tgsi_full_immediate *imm ) +{ + struct dump_ctx *ctx = (struct dump_ctx *) iter; + + TXT( "IMM[" ); + SID( ctx->immno++ ); + TXT( "] " ); + ENM( imm->Immediate.DataType, tgsi_immediate_type_names ); + + dump_imm_data(iter, imm->u, imm->Immediate.NrTokens - 1, + imm->Immediate.DataType); + + EOL(); + + return TRUE; +} + +void +tgsi_dump_immediate( + const struct tgsi_full_immediate *imm ) +{ + struct dump_ctx ctx; + + ctx.dump_printf = dump_ctx_printf; + + iter_immediate( &ctx.iter, (struct tgsi_full_immediate *)imm ); +} + +static boolean +iter_instruction( + struct tgsi_iterate_context *iter, + struct tgsi_full_instruction *inst ) +{ + struct dump_ctx *ctx = (struct dump_ctx *) iter; + uint instno = ctx->instno++; + const struct tgsi_opcode_info *info = tgsi_get_opcode_info( inst->Instruction.Opcode ); + uint i; + boolean first_reg = TRUE; + + INSTID( instno ); + TXT( ": " ); + + ctx->indent -= info->pre_dedent; + for(i = 0; (int)i < ctx->indent; ++i) + TXT( " " ); + ctx->indent += info->post_indent; + + TXT( info->mnemonic ); + + if (inst->Instruction.Saturate) { + TXT( "_SAT" ); + } + + for (i = 0; i < inst->Instruction.NumDstRegs; i++) { + const struct tgsi_full_dst_register *dst = &inst->Dst[i]; + + if (!first_reg) + CHR( ',' ); + CHR( ' ' ); + + _dump_register_dst( ctx, dst ); + _dump_writemask( ctx, dst->Register.WriteMask ); + + first_reg = FALSE; + } + + for (i = 0; i < inst->Instruction.NumSrcRegs; i++) { + const struct tgsi_full_src_register *src = &inst->Src[i]; + + if (!first_reg) + CHR( ',' ); + CHR( ' ' ); + + if (src->Register.Negate) + CHR( '-' ); + if (src->Register.Absolute) + CHR( '|' ); + + _dump_register_src(ctx, src); + + if (src->Register.SwizzleX != TGSI_SWIZZLE_X || + src->Register.SwizzleY != TGSI_SWIZZLE_Y || + src->Register.SwizzleZ != TGSI_SWIZZLE_Z || + src->Register.SwizzleW != TGSI_SWIZZLE_W) { + CHR( '.' ); + ENM( src->Register.SwizzleX, tgsi_swizzle_names ); + ENM( src->Register.SwizzleY, tgsi_swizzle_names ); + ENM( src->Register.SwizzleZ, tgsi_swizzle_names ); + ENM( src->Register.SwizzleW, tgsi_swizzle_names ); + } + + if (src->Register.Absolute) + CHR( '|' ); + + first_reg = FALSE; + } + + if (inst->Instruction.Texture) { + if (!(inst->Instruction.Opcode >= TGSI_OPCODE_SAMPLE && + inst->Instruction.Opcode <= TGSI_OPCODE_GATHER4)) { + TXT( ", " ); + ENM( inst->Texture.Texture, tgsi_texture_names ); + } + for (i = 0; i < inst->Texture.NumOffsets; i++) { + TXT( ", " ); + TXT(tgsi_file_name(inst->TexOffsets[i].File)); + CHR( '[' ); + SID( inst->TexOffsets[i].Index ); + CHR( ']' ); + CHR( '.' ); + ENM( inst->TexOffsets[i].SwizzleX, tgsi_swizzle_names); + ENM( inst->TexOffsets[i].SwizzleY, tgsi_swizzle_names); + ENM( inst->TexOffsets[i].SwizzleZ, tgsi_swizzle_names); + } + } + + if (inst->Instruction.Memory) { + uint32_t qualifier = inst->Memory.Qualifier; + while (qualifier) { + int bit = ffs(qualifier) - 1; + qualifier &= ~(1U << bit); + TXT(", "); + ENM(bit, tgsi_memory_names); + } + if (inst->Memory.Texture) { + TXT( ", " ); + ENM( inst->Memory.Texture, tgsi_texture_names ); + } + if (inst->Memory.Format) { + TXT( ", " ); + TXT( util_format_name(inst->Memory.Format) ); + } + } + + if (inst->Instruction.Label) { + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_IF: + case TGSI_OPCODE_UIF: + case TGSI_OPCODE_ELSE: + case TGSI_OPCODE_BGNLOOP: + case TGSI_OPCODE_ENDLOOP: + case TGSI_OPCODE_CAL: + case TGSI_OPCODE_BGNSUB: + TXT( " :" ); + UID( inst->Label.Label ); + break; + } + } + + /* update indentation */ + if (inst->Instruction.Opcode == TGSI_OPCODE_IF || + inst->Instruction.Opcode == TGSI_OPCODE_UIF || + inst->Instruction.Opcode == TGSI_OPCODE_ELSE || + inst->Instruction.Opcode == TGSI_OPCODE_BGNLOOP) { + ctx->indentation += indent_spaces; + } + + EOL(); + + return TRUE; +} + +void +tgsi_dump_instruction( + const struct tgsi_full_instruction *inst, + uint instno ) +{ + struct dump_ctx ctx; + + ctx.instno = instno; + ctx.immno = instno; + ctx.indent = 0; + ctx.dump_printf = dump_ctx_printf; + ctx.indentation = 0; + ctx.file = NULL; + + iter_instruction( &ctx.iter, (struct tgsi_full_instruction *)inst ); +} + +static boolean +prolog( + struct tgsi_iterate_context *iter ) +{ + struct dump_ctx *ctx = (struct dump_ctx *) iter; + ENM( iter->processor.Processor, tgsi_processor_type_names ); + EOL(); + return TRUE; +} + +void +tgsi_dump_to_file(const struct tgsi_token *tokens, uint flags, FILE *file) +{ + struct dump_ctx ctx; + + ctx.iter.prolog = prolog; + ctx.iter.iterate_instruction = iter_instruction; + ctx.iter.iterate_declaration = iter_declaration; + ctx.iter.iterate_immediate = iter_immediate; + ctx.iter.iterate_property = iter_property; + ctx.iter.epilog = NULL; + + ctx.instno = 0; + ctx.immno = 0; + ctx.indent = 0; + ctx.dump_printf = dump_ctx_printf; + ctx.indentation = 0; + ctx.file = file; + + if (flags & TGSI_DUMP_FLOAT_AS_HEX) + ctx.dump_float_as_hex = TRUE; + else + ctx.dump_float_as_hex = FALSE; + + tgsi_iterate_shader( tokens, &ctx.iter ); +} + +void +tgsi_dump(const struct tgsi_token *tokens, uint flags) +{ + tgsi_dump_to_file(tokens, flags, NULL); +} + +struct str_dump_ctx +{ + struct dump_ctx base; + char *str; + char *ptr; + int left; + bool nospace; +}; + +static void +str_dump_ctx_printf(struct dump_ctx *ctx, const char *format, ...) +{ + struct str_dump_ctx *sctx = (struct str_dump_ctx *)ctx; + + if(sctx->left > 1) { + int written; + va_list ap; + va_start(ap, format); + written = util_vsnprintf(sctx->ptr, sctx->left, format, ap); + va_end(ap); + + /* Some complicated logic needed to handle the return value of + * vsnprintf: + */ + if (written > 0) { + written = MIN2(sctx->left, written); + sctx->ptr += written; + sctx->left -= written; + } + } else + sctx->nospace = true; +} + +bool +tgsi_dump_str( + const struct tgsi_token *tokens, + uint flags, + char *str, + size_t size) +{ + struct str_dump_ctx ctx; + + ctx.base.iter.prolog = prolog; + ctx.base.iter.iterate_instruction = iter_instruction; + ctx.base.iter.iterate_declaration = iter_declaration; + ctx.base.iter.iterate_immediate = iter_immediate; + ctx.base.iter.iterate_property = iter_property; + ctx.base.iter.epilog = NULL; + + ctx.base.instno = 0; + ctx.base.immno = 0; + ctx.base.indent = 0; + ctx.base.dump_printf = &str_dump_ctx_printf; + ctx.base.indentation = 0; + ctx.base.file = NULL; + + ctx.str = str; + ctx.str[0] = 0; + ctx.ptr = str; + ctx.left = (int)size; + ctx.nospace = false; + + if (flags & TGSI_DUMP_FLOAT_AS_HEX) + ctx.base.dump_float_as_hex = TRUE; + else + ctx.base.dump_float_as_hex = FALSE; + + tgsi_iterate_shader( tokens, &ctx.base.iter ); + + return !ctx.nospace; +} + +void +tgsi_dump_instruction_str( + const struct tgsi_full_instruction *inst, + uint instno, + char *str, + size_t size) +{ + struct str_dump_ctx ctx; + + ctx.base.instno = instno; + ctx.base.immno = instno; + ctx.base.indent = 0; + ctx.base.dump_printf = &str_dump_ctx_printf; + ctx.base.indentation = 0; + ctx.base.file = NULL; + + ctx.str = str; + ctx.str[0] = 0; + ctx.ptr = str; + ctx.left = (int)size; + ctx.nospace = false; + + iter_instruction( &ctx.base.iter, (struct tgsi_full_instruction *)inst ); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_dump.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_dump.h new file mode 100644 index 0000000..c3722d3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_dump.h @@ -0,0 +1,91 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_DUMP_H +#define TGSI_DUMP_H + +#include "pipe/p_compiler.h" +#include "pipe/p_defines.h" +#include "pipe/p_shader_tokens.h" + +#include + +#if defined __cplusplus +extern "C" { +#endif + +#define TGSI_DUMP_FLOAT_AS_HEX (1 << 0) + +bool +tgsi_dump_str( + const struct tgsi_token *tokens, + uint flags, + char *str, + size_t size); + +void +tgsi_dump_to_file(const struct tgsi_token *tokens, uint flags, FILE *file); + +void +tgsi_dump( + const struct tgsi_token *tokens, + uint flags ); + +struct tgsi_full_immediate; +struct tgsi_full_instruction; +struct tgsi_full_declaration; +struct tgsi_full_property; + +void +tgsi_dump_immediate( + const struct tgsi_full_immediate *imm ); + +void +tgsi_dump_instruction_str( + const struct tgsi_full_instruction *inst, + uint instno, + char *str, + size_t size); + +void +tgsi_dump_instruction( + const struct tgsi_full_instruction *inst, + uint instno ); + +void +tgsi_dump_declaration( + const struct tgsi_full_declaration *decl ); + +void +tgsi_dump_property( + const struct tgsi_full_property *prop ); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_DUMP_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_info.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_info.c new file mode 100644 index 0000000..7cac984 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_info.c @@ -0,0 +1,554 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "util/u_memory.h" +#include "tgsi_info.h" + +#define NONE TGSI_OUTPUT_NONE +#define COMP TGSI_OUTPUT_COMPONENTWISE +#define REPL TGSI_OUTPUT_REPLICATE +#define CHAN TGSI_OUTPUT_CHAN_DEPENDENT +#define OTHR TGSI_OUTPUT_OTHER + +static const struct tgsi_opcode_info opcode_info[TGSI_OPCODE_LAST] = +{ + { 1, 1, 0, 0, 0, 0, COMP, "ARL", TGSI_OPCODE_ARL }, + { 1, 1, 0, 0, 0, 0, COMP, "MOV", TGSI_OPCODE_MOV }, + { 1, 1, 0, 0, 0, 0, CHAN, "LIT", TGSI_OPCODE_LIT }, + { 1, 1, 0, 0, 0, 0, REPL, "RCP", TGSI_OPCODE_RCP }, + { 1, 1, 0, 0, 0, 0, REPL, "RSQ", TGSI_OPCODE_RSQ }, + { 1, 1, 0, 0, 0, 0, CHAN, "EXP", TGSI_OPCODE_EXP }, + { 1, 1, 0, 0, 0, 0, CHAN, "LOG", TGSI_OPCODE_LOG }, + { 1, 2, 0, 0, 0, 0, COMP, "MUL", TGSI_OPCODE_MUL }, + { 1, 2, 0, 0, 0, 0, COMP, "ADD", TGSI_OPCODE_ADD }, + { 1, 2, 0, 0, 0, 0, REPL, "DP3", TGSI_OPCODE_DP3 }, + { 1, 2, 0, 0, 0, 0, REPL, "DP4", TGSI_OPCODE_DP4 }, + { 1, 2, 0, 0, 0, 0, CHAN, "DST", TGSI_OPCODE_DST }, + { 1, 2, 0, 0, 0, 0, COMP, "MIN", TGSI_OPCODE_MIN }, + { 1, 2, 0, 0, 0, 0, COMP, "MAX", TGSI_OPCODE_MAX }, + { 1, 2, 0, 0, 0, 0, COMP, "SLT", TGSI_OPCODE_SLT }, + { 1, 2, 0, 0, 0, 0, COMP, "SGE", TGSI_OPCODE_SGE }, + { 1, 3, 0, 0, 0, 0, COMP, "MAD", TGSI_OPCODE_MAD }, + { 1, 2, 0, 0, 0, 0, COMP, "SUB", TGSI_OPCODE_SUB }, + { 1, 3, 0, 0, 0, 0, COMP, "LRP", TGSI_OPCODE_LRP }, + { 1, 3, 0, 0, 0, 0, COMP, "FMA", TGSI_OPCODE_FMA }, + { 1, 1, 0, 0, 0, 0, REPL, "SQRT", TGSI_OPCODE_SQRT }, + { 1, 3, 0, 0, 0, 0, REPL, "", 21 }, /* removed */ + { 0, 0, 0, 0, 0, 0, NONE, "", 22 }, /* removed */ + { 0, 0, 0, 0, 0, 0, NONE, "", 23 }, /* removed */ + { 1, 1, 0, 0, 0, 0, COMP, "FRC", TGSI_OPCODE_FRC }, + { 1, 3, 0, 0, 0, 0, COMP, "", 25 }, /* removed */ + { 1, 1, 0, 0, 0, 0, COMP, "FLR", TGSI_OPCODE_FLR }, + { 1, 1, 0, 0, 0, 0, COMP, "ROUND", TGSI_OPCODE_ROUND }, + { 1, 1, 0, 0, 0, 0, REPL, "EX2", TGSI_OPCODE_EX2 }, + { 1, 1, 0, 0, 0, 0, REPL, "LG2", TGSI_OPCODE_LG2 }, + { 1, 2, 0, 0, 0, 0, REPL, "POW", TGSI_OPCODE_POW }, + { 1, 2, 0, 0, 0, 0, COMP, "XPD", TGSI_OPCODE_XPD }, + { 0, 0, 0, 0, 0, 0, NONE, "", 32 }, /* removed */ + { 1, 1, 0, 0, 0, 0, COMP, "ABS", TGSI_OPCODE_ABS }, + { 0, 0, 0, 0, 0, 0, NONE, "", 34 }, /* removed */ + { 1, 2, 0, 0, 0, 0, REPL, "DPH", TGSI_OPCODE_DPH }, + { 1, 1, 0, 0, 0, 0, REPL, "COS", TGSI_OPCODE_COS }, + { 1, 1, 0, 0, 0, 0, COMP, "DDX", TGSI_OPCODE_DDX }, + { 1, 1, 0, 0, 0, 0, COMP, "DDY", TGSI_OPCODE_DDY }, + { 0, 0, 0, 0, 0, 0, NONE, "KILL", TGSI_OPCODE_KILL }, + { 1, 1, 0, 0, 0, 0, COMP, "PK2H", TGSI_OPCODE_PK2H }, + { 1, 1, 0, 0, 0, 0, COMP, "PK2US", TGSI_OPCODE_PK2US }, + { 1, 1, 0, 0, 0, 0, COMP, "PK4B", TGSI_OPCODE_PK4B }, + { 1, 1, 0, 0, 0, 0, COMP, "PK4UB", TGSI_OPCODE_PK4UB }, + { 0, 1, 0, 0, 0, 1, NONE, "", 44 }, /* removed */ + { 1, 2, 0, 0, 0, 0, COMP, "SEQ", TGSI_OPCODE_SEQ }, + { 0, 1, 0, 0, 0, 1, NONE, "", 46 }, /* removed */ + { 1, 2, 0, 0, 0, 0, COMP, "SGT", TGSI_OPCODE_SGT }, + { 1, 1, 0, 0, 0, 0, REPL, "SIN", TGSI_OPCODE_SIN }, + { 1, 2, 0, 0, 0, 0, COMP, "SLE", TGSI_OPCODE_SLE }, + { 1, 2, 0, 0, 0, 0, COMP, "SNE", TGSI_OPCODE_SNE }, + { 0, 1, 0, 0, 0, 1, NONE, "", 51 }, /* removed */ + { 1, 2, 1, 0, 0, 0, OTHR, "TEX", TGSI_OPCODE_TEX }, + { 1, 4, 1, 0, 0, 0, OTHR, "TXD", TGSI_OPCODE_TXD }, + { 1, 2, 1, 0, 0, 0, OTHR, "TXP", TGSI_OPCODE_TXP }, + { 1, 1, 0, 0, 0, 0, COMP, "UP2H", TGSI_OPCODE_UP2H }, + { 1, 1, 0, 0, 0, 0, COMP, "UP2US", TGSI_OPCODE_UP2US }, + { 1, 1, 0, 0, 0, 0, COMP, "UP4B", TGSI_OPCODE_UP4B }, + { 1, 1, 0, 0, 0, 0, COMP, "UP4UB", TGSI_OPCODE_UP4UB }, + { 0, 1, 0, 0, 0, 1, NONE, "", 59 }, /* removed */ + { 0, 1, 0, 0, 0, 1, NONE, "", 60 }, /* removed */ + { 1, 1, 0, 0, 0, 0, COMP, "ARR", TGSI_OPCODE_ARR }, + { 0, 1, 0, 0, 0, 1, NONE, "", 62 }, /* removed */ + { 0, 0, 0, 1, 0, 0, NONE, "CAL", TGSI_OPCODE_CAL }, + { 0, 0, 0, 0, 0, 0, NONE, "RET", TGSI_OPCODE_RET }, + { 1, 1, 0, 0, 0, 0, COMP, "SSG", TGSI_OPCODE_SSG }, + { 1, 3, 0, 0, 0, 0, COMP, "CMP", TGSI_OPCODE_CMP }, + { 1, 1, 0, 0, 0, 0, CHAN, "SCS", TGSI_OPCODE_SCS }, + { 1, 2, 1, 0, 0, 0, OTHR, "TXB", TGSI_OPCODE_TXB }, + { 1, 1, 0, 0, 0, 0, OTHR, "FBFETCH", TGSI_OPCODE_FBFETCH }, + { 1, 2, 0, 0, 0, 0, COMP, "DIV", TGSI_OPCODE_DIV }, + { 1, 2, 0, 0, 0, 0, REPL, "DP2", TGSI_OPCODE_DP2 }, + { 1, 2, 1, 0, 0, 0, OTHR, "TXL", TGSI_OPCODE_TXL }, + { 0, 0, 0, 0, 0, 0, NONE, "BRK", TGSI_OPCODE_BRK }, + { 0, 1, 0, 1, 0, 1, NONE, "IF", TGSI_OPCODE_IF }, + { 0, 1, 0, 1, 0, 1, NONE, "UIF", TGSI_OPCODE_UIF }, + { 0, 1, 0, 0, 0, 1, NONE, "", 76 }, /* removed */ + { 0, 0, 0, 1, 1, 1, NONE, "ELSE", TGSI_OPCODE_ELSE }, + { 0, 0, 0, 0, 1, 0, NONE, "ENDIF", TGSI_OPCODE_ENDIF }, + { 1, 1, 0, 0, 0, 0, COMP, "DDX_FINE", TGSI_OPCODE_DDX_FINE }, + { 1, 1, 0, 0, 0, 0, COMP, "DDY_FINE", TGSI_OPCODE_DDY_FINE }, + { 0, 0, 0, 0, 0, 0, NONE, "", 81 }, /* removed */ + { 0, 0, 0, 0, 0, 0, NONE, "", 82 }, /* removed */ + { 1, 1, 0, 0, 0, 0, COMP, "CEIL", TGSI_OPCODE_CEIL }, + { 1, 1, 0, 0, 0, 0, COMP, "I2F", TGSI_OPCODE_I2F }, + { 1, 1, 0, 0, 0, 0, COMP, "NOT", TGSI_OPCODE_NOT }, + { 1, 1, 0, 0, 0, 0, COMP, "TRUNC", TGSI_OPCODE_TRUNC }, + { 1, 2, 0, 0, 0, 0, COMP, "SHL", TGSI_OPCODE_SHL }, + { 0, 0, 0, 0, 0, 0, NONE, "", 88 }, /* removed */ + { 1, 2, 0, 0, 0, 0, COMP, "AND", TGSI_OPCODE_AND }, + { 1, 2, 0, 0, 0, 0, COMP, "OR", TGSI_OPCODE_OR }, + { 1, 2, 0, 0, 0, 0, COMP, "MOD", TGSI_OPCODE_MOD }, + { 1, 2, 0, 0, 0, 0, COMP, "XOR", TGSI_OPCODE_XOR }, + { 0, 0, 0, 0, 0, 0, COMP, "", 93 }, /* removed */ + { 1, 2, 1, 0, 0, 0, OTHR, "TXF", TGSI_OPCODE_TXF }, + { 1, 2, 1, 0, 0, 0, OTHR, "TXQ", TGSI_OPCODE_TXQ }, + { 0, 0, 0, 0, 0, 0, NONE, "CONT", TGSI_OPCODE_CONT }, + { 0, 1, 0, 0, 0, 0, NONE, "EMIT", TGSI_OPCODE_EMIT }, + { 0, 1, 0, 0, 0, 0, NONE, "ENDPRIM", TGSI_OPCODE_ENDPRIM }, + { 0, 0, 0, 1, 0, 1, NONE, "BGNLOOP", TGSI_OPCODE_BGNLOOP }, + { 0, 0, 0, 0, 0, 1, NONE, "BGNSUB", TGSI_OPCODE_BGNSUB }, + { 0, 0, 0, 1, 1, 0, NONE, "ENDLOOP", TGSI_OPCODE_ENDLOOP }, + { 0, 0, 0, 0, 1, 0, NONE, "ENDSUB", TGSI_OPCODE_ENDSUB }, + { 0, 0, 0, 0, 0, 0, OTHR, "", 103 }, /* removed */ + { 1, 1, 1, 0, 0, 0, OTHR, "TXQS", TGSI_OPCODE_TXQS }, + { 1, 1, 0, 0, 0, 0, OTHR, "RESQ", TGSI_OPCODE_RESQ }, + { 0, 0, 0, 0, 0, 0, NONE, "", 106 }, /* removed */ + { 0, 0, 0, 0, 0, 0, NONE, "NOP", TGSI_OPCODE_NOP }, + { 1, 2, 0, 0, 0, 0, COMP, "FSEQ", TGSI_OPCODE_FSEQ }, + { 1, 2, 0, 0, 0, 0, COMP, "FSGE", TGSI_OPCODE_FSGE }, + { 1, 2, 0, 0, 0, 0, COMP, "FSLT", TGSI_OPCODE_FSLT }, + { 1, 2, 0, 0, 0, 0, COMP, "FSNE", TGSI_OPCODE_FSNE }, + { 0, 1, 0, 0, 0, 0, OTHR, "MEMBAR", TGSI_OPCODE_MEMBAR }, + { 0, 1, 0, 0, 0, 0, NONE, "", 113 }, /* removed */ + { 0, 1, 0, 0, 0, 0, NONE, "", 114 }, /* removed */ + { 0, 1, 0, 0, 0, 0, NONE, "", 115 }, /* removed */ + { 0, 1, 0, 0, 0, 0, NONE, "KILL_IF", TGSI_OPCODE_KILL_IF }, + { 0, 0, 0, 0, 0, 0, NONE, "END", TGSI_OPCODE_END }, + { 1, 3, 0, 0, 0, 0, COMP, "DFMA", TGSI_OPCODE_DFMA }, + { 1, 1, 0, 0, 0, 0, COMP, "F2I", TGSI_OPCODE_F2I }, + { 1, 2, 0, 0, 0, 0, COMP, "IDIV", TGSI_OPCODE_IDIV }, + { 1, 2, 0, 0, 0, 0, COMP, "IMAX", TGSI_OPCODE_IMAX }, + { 1, 2, 0, 0, 0, 0, COMP, "IMIN", TGSI_OPCODE_IMIN }, + { 1, 1, 0, 0, 0, 0, COMP, "INEG", TGSI_OPCODE_INEG }, + { 1, 2, 0, 0, 0, 0, COMP, "ISGE", TGSI_OPCODE_ISGE }, + { 1, 2, 0, 0, 0, 0, COMP, "ISHR", TGSI_OPCODE_ISHR }, + { 1, 2, 0, 0, 0, 0, COMP, "ISLT", TGSI_OPCODE_ISLT }, + { 1, 1, 0, 0, 0, 0, COMP, "F2U", TGSI_OPCODE_F2U }, + { 1, 1, 0, 0, 0, 0, COMP, "U2F", TGSI_OPCODE_U2F }, + { 1, 2, 0, 0, 0, 0, COMP, "UADD", TGSI_OPCODE_UADD }, + { 1, 2, 0, 0, 0, 0, COMP, "UDIV", TGSI_OPCODE_UDIV }, + { 1, 3, 0, 0, 0, 0, COMP, "UMAD", TGSI_OPCODE_UMAD }, + { 1, 2, 0, 0, 0, 0, COMP, "UMAX", TGSI_OPCODE_UMAX }, + { 1, 2, 0, 0, 0, 0, COMP, "UMIN", TGSI_OPCODE_UMIN }, + { 1, 2, 0, 0, 0, 0, COMP, "UMOD", TGSI_OPCODE_UMOD }, + { 1, 2, 0, 0, 0, 0, COMP, "UMUL", TGSI_OPCODE_UMUL }, + { 1, 2, 0, 0, 0, 0, COMP, "USEQ", TGSI_OPCODE_USEQ }, + { 1, 2, 0, 0, 0, 0, COMP, "USGE", TGSI_OPCODE_USGE }, + { 1, 2, 0, 0, 0, 0, COMP, "USHR", TGSI_OPCODE_USHR }, + { 1, 2, 0, 0, 0, 0, COMP, "USLT", TGSI_OPCODE_USLT }, + { 1, 2, 0, 0, 0, 0, COMP, "USNE", TGSI_OPCODE_USNE }, + { 0, 1, 0, 0, 0, 0, NONE, "SWITCH", TGSI_OPCODE_SWITCH }, + { 0, 1, 0, 0, 0, 0, NONE, "CASE", TGSI_OPCODE_CASE }, + { 0, 0, 0, 0, 0, 0, NONE, "DEFAULT", TGSI_OPCODE_DEFAULT }, + { 0, 0, 0, 0, 0, 0, NONE, "ENDSWITCH", TGSI_OPCODE_ENDSWITCH }, + + { 1, 3, 0, 0, 0, 0, OTHR, "SAMPLE", TGSI_OPCODE_SAMPLE }, + { 1, 2, 0, 0, 0, 0, OTHR, "SAMPLE_I", TGSI_OPCODE_SAMPLE_I }, + { 1, 3, 0, 0, 0, 0, OTHR, "SAMPLE_I_MS", TGSI_OPCODE_SAMPLE_I_MS }, + { 1, 4, 0, 0, 0, 0, OTHR, "SAMPLE_B", TGSI_OPCODE_SAMPLE_B }, + { 1, 4, 0, 0, 0, 0, OTHR, "SAMPLE_C", TGSI_OPCODE_SAMPLE_C }, + { 1, 4, 0, 0, 0, 0, OTHR, "SAMPLE_C_LZ", TGSI_OPCODE_SAMPLE_C_LZ }, + { 1, 5, 0, 0, 0, 0, OTHR, "SAMPLE_D", TGSI_OPCODE_SAMPLE_D }, + { 1, 4, 0, 0, 0, 0, OTHR, "SAMPLE_L", TGSI_OPCODE_SAMPLE_L }, + { 1, 3, 0, 0, 0, 0, OTHR, "GATHER4", TGSI_OPCODE_GATHER4 }, + { 1, 2, 0, 0, 0, 0, OTHR, "SVIEWINFO", TGSI_OPCODE_SVIEWINFO }, + { 1, 2, 0, 0, 0, 0, OTHR, "SAMPLE_POS", TGSI_OPCODE_SAMPLE_POS }, + { 1, 2, 0, 0, 0, 0, OTHR, "SAMPLE_INFO", TGSI_OPCODE_SAMPLE_INFO }, + { 1, 1, 0, 0, 0, 0, COMP, "UARL", TGSI_OPCODE_UARL }, + { 1, 3, 0, 0, 0, 0, COMP, "UCMP", TGSI_OPCODE_UCMP }, + { 1, 1, 0, 0, 0, 0, COMP, "IABS", TGSI_OPCODE_IABS }, + { 1, 1, 0, 0, 0, 0, COMP, "ISSG", TGSI_OPCODE_ISSG }, + { 1, 2, 0, 0, 0, 0, OTHR, "LOAD", TGSI_OPCODE_LOAD }, + { 1, 2, 0, 0, 0, 0, OTHR, "STORE", TGSI_OPCODE_STORE }, + { 1, 0, 0, 0, 0, 0, OTHR, "", 163 }, + { 1, 0, 0, 0, 0, 0, OTHR, "", 164 }, + { 1, 0, 0, 0, 0, 0, OTHR, "", 165 }, + { 0, 0, 0, 0, 0, 0, OTHR, "BARRIER", TGSI_OPCODE_BARRIER }, + + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMUADD", TGSI_OPCODE_ATOMUADD }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMXCHG", TGSI_OPCODE_ATOMXCHG }, + { 1, 4, 0, 0, 0, 0, OTHR, "ATOMCAS", TGSI_OPCODE_ATOMCAS }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMAND", TGSI_OPCODE_ATOMAND }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMOR", TGSI_OPCODE_ATOMOR }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMXOR", TGSI_OPCODE_ATOMXOR }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMUMIN", TGSI_OPCODE_ATOMUMIN }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMUMAX", TGSI_OPCODE_ATOMUMAX }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMIMIN", TGSI_OPCODE_ATOMIMIN }, + { 1, 3, 0, 0, 0, 0, OTHR, "ATOMIMAX", TGSI_OPCODE_ATOMIMAX }, + { 1, 3, 1, 0, 0, 0, OTHR, "TEX2", TGSI_OPCODE_TEX2 }, + { 1, 3, 1, 0, 0, 0, OTHR, "TXB2", TGSI_OPCODE_TXB2 }, + { 1, 3, 1, 0, 0, 0, OTHR, "TXL2", TGSI_OPCODE_TXL2 }, + { 1, 2, 0, 0, 0, 0, COMP, "IMUL_HI", TGSI_OPCODE_IMUL_HI }, + { 1, 2, 0, 0, 0, 0, COMP, "UMUL_HI", TGSI_OPCODE_UMUL_HI }, + { 1, 3, 1, 0, 0, 0, OTHR, "TG4", TGSI_OPCODE_TG4 }, + { 1, 2, 1, 0, 0, 0, OTHR, "LODQ", TGSI_OPCODE_LODQ }, + { 1, 3, 0, 0, 0, 0, COMP, "IBFE", TGSI_OPCODE_IBFE }, + { 1, 3, 0, 0, 0, 0, COMP, "UBFE", TGSI_OPCODE_UBFE }, + { 1, 4, 0, 0, 0, 0, COMP, "BFI", TGSI_OPCODE_BFI }, + { 1, 1, 0, 0, 0, 0, COMP, "BREV", TGSI_OPCODE_BREV }, + { 1, 1, 0, 0, 0, 0, COMP, "POPC", TGSI_OPCODE_POPC }, + { 1, 1, 0, 0, 0, 0, COMP, "LSB", TGSI_OPCODE_LSB }, + { 1, 1, 0, 0, 0, 0, COMP, "IMSB", TGSI_OPCODE_IMSB }, + { 1, 1, 0, 0, 0, 0, COMP, "UMSB", TGSI_OPCODE_UMSB }, + { 1, 1, 0, 0, 0, 0, OTHR, "INTERP_CENTROID", TGSI_OPCODE_INTERP_CENTROID }, + { 1, 2, 0, 0, 0, 0, OTHR, "INTERP_SAMPLE", TGSI_OPCODE_INTERP_SAMPLE }, + { 1, 2, 0, 0, 0, 0, OTHR, "INTERP_OFFSET", TGSI_OPCODE_INTERP_OFFSET }, + { 1, 1, 0, 0, 0, 0, COMP, "F2D", TGSI_OPCODE_F2D }, + { 1, 1, 0, 0, 0, 0, COMP, "D2F", TGSI_OPCODE_D2F }, + { 1, 1, 0, 0, 0, 0, COMP, "DABS", TGSI_OPCODE_DABS }, + { 1, 1, 0, 0, 0, 0, COMP, "DNEG", TGSI_OPCODE_DNEG }, + { 1, 2, 0, 0, 0, 0, COMP, "DADD", TGSI_OPCODE_DADD }, + { 1, 2, 0, 0, 0, 0, COMP, "DMUL", TGSI_OPCODE_DMUL }, + { 1, 2, 0, 0, 0, 0, COMP, "DMAX", TGSI_OPCODE_DMAX }, + { 1, 2, 0, 0, 0, 0, COMP, "DMIN", TGSI_OPCODE_DMIN }, + { 1, 2, 0, 0, 0, 0, COMP, "DSLT", TGSI_OPCODE_DSLT }, + { 1, 2, 0, 0, 0, 0, COMP, "DSGE", TGSI_OPCODE_DSGE }, + { 1, 2, 0, 0, 0, 0, COMP, "DSEQ", TGSI_OPCODE_DSEQ }, + { 1, 2, 0, 0, 0, 0, COMP, "DSNE", TGSI_OPCODE_DSNE }, + { 1, 1, 0, 0, 0, 0, COMP, "DRCP", TGSI_OPCODE_DRCP }, + { 1, 1, 0, 0 ,0, 0, COMP, "DSQRT", TGSI_OPCODE_DSQRT }, + { 1, 3, 0, 0 ,0, 0, COMP, "DMAD", TGSI_OPCODE_DMAD }, + { 1, 1, 0, 0, 0, 0, COMP, "DFRAC", TGSI_OPCODE_DFRAC}, + { 1, 2, 0, 0, 0, 0, COMP, "DLDEXP", TGSI_OPCODE_DLDEXP}, + { 2, 1, 0, 0, 0, 0, COMP, "DFRACEXP", TGSI_OPCODE_DFRACEXP}, + { 1, 1, 0, 0, 0, 0, COMP, "D2I", TGSI_OPCODE_D2I }, + { 1, 1, 0, 0, 0, 0, COMP, "I2D", TGSI_OPCODE_I2D }, + { 1, 1, 0, 0, 0, 0, COMP, "D2U", TGSI_OPCODE_D2U }, + { 1, 1, 0, 0, 0, 0, COMP, "U2D", TGSI_OPCODE_U2D }, + { 1, 1, 0, 0 ,0, 0, COMP, "DRSQ", TGSI_OPCODE_DRSQ }, + { 1, 1, 0, 0, 0, 0, COMP, "DTRUNC", TGSI_OPCODE_DTRUNC }, + { 1, 1, 0, 0, 0, 0, COMP, "DCEIL", TGSI_OPCODE_DCEIL }, + { 1, 1, 0, 0, 0, 0, COMP, "DFLR", TGSI_OPCODE_DFLR }, + { 1, 1, 0, 0, 0, 0, COMP, "DROUND", TGSI_OPCODE_DROUND }, + { 1, 1, 0, 0, 0, 0, COMP, "DSSG", TGSI_OPCODE_DSSG }, + { 1, 2, 0, 0, 0, 0, COMP, "DDIV", TGSI_OPCODE_DDIV }, + { 1, 0, 0, 0, 0, 0, OTHR, "CLOCK", TGSI_OPCODE_CLOCK }, + + { 1, 1, 0, 0, 0, 0, COMP, "I64ABS", TGSI_OPCODE_I64ABS }, + { 1, 1, 0, 0, 0, 0, COMP, "I64NEG", TGSI_OPCODE_I64NEG }, + { 1, 1, 0, 0, 0, 0, COMP, "I64SSG", TGSI_OPCODE_I64SSG }, + { 1, 2, 0, 0, 0, 0, COMP, "I64SLT", TGSI_OPCODE_I64SLT }, + { 1, 2, 0, 0, 0, 0, COMP, "I64SGE", TGSI_OPCODE_I64SGE }, + { 1, 2, 0, 0, 0, 0, COMP, "I64MIN", TGSI_OPCODE_I64MIN }, + { 1, 2, 0, 0, 0, 0, COMP, "I64MAX", TGSI_OPCODE_I64MAX }, + { 1, 2, 0, 0, 0, 0, COMP, "I64SHR", TGSI_OPCODE_I64SHR }, + { 1, 2, 0, 0, 0, 0, COMP, "I64DIV", TGSI_OPCODE_I64DIV }, + { 1, 2, 0, 0, 0, 0, COMP, "I64MOD", TGSI_OPCODE_I64MOD }, + { 1, 1, 0, 0, 0, 0, COMP, "F2I64", TGSI_OPCODE_F2I64 }, + { 1, 1, 0, 0, 0, 0, COMP, "U2I64", TGSI_OPCODE_U2I64 }, + { 1, 1, 0, 0, 0, 0, COMP, "I2I64", TGSI_OPCODE_I2I64 }, + { 1, 1, 0, 0, 0, 0, COMP, "D2I64", TGSI_OPCODE_D2I64 }, + { 1, 1, 0, 0, 0, 0, COMP, "I642F", TGSI_OPCODE_I642F }, + { 1, 1, 0, 0, 0, 0, COMP, "I642D", TGSI_OPCODE_I642D }, + + { 1, 2, 0, 0, 0, 0, COMP, "U64ADD", TGSI_OPCODE_U64ADD }, + { 1, 2, 0, 0, 0, 0, COMP, "U64MUL", TGSI_OPCODE_U64MUL }, + { 1, 2, 0, 0, 0, 0, COMP, "U64SEQ", TGSI_OPCODE_U64SEQ }, + { 1, 2, 0, 0, 0, 0, COMP, "U64SNE", TGSI_OPCODE_U64SNE }, + { 1, 2, 0, 0, 0, 0, COMP, "U64SLT", TGSI_OPCODE_U64SLT }, + { 1, 2, 0, 0, 0, 0, COMP, "U64SGE", TGSI_OPCODE_U64SGE }, + { 1, 2, 0, 0, 0, 0, COMP, "U64MIN", TGSI_OPCODE_U64MIN }, + { 1, 2, 0, 0, 0, 0, COMP, "U64MAX", TGSI_OPCODE_U64MAX }, + { 1, 2, 0, 0, 0, 0, COMP, "U64SHL", TGSI_OPCODE_U64SHL }, + { 1, 2, 0, 0, 0, 0, COMP, "U64SHR", TGSI_OPCODE_U64SHR }, + { 1, 2, 0, 0, 0, 0, COMP, "U64DIV", TGSI_OPCODE_U64DIV }, + { 1, 2, 0, 0, 0, 0, COMP, "U64MOD", TGSI_OPCODE_U64MOD }, + { 1, 1, 0, 0, 0, 0, COMP, "F2U64", TGSI_OPCODE_F2U64 }, + { 1, 1, 0, 0, 0, 0, COMP, "D2U64", TGSI_OPCODE_D2U64 }, + { 1, 1, 0, 0, 0, 0, COMP, "U642F", TGSI_OPCODE_U642F }, + { 1, 1, 0, 0, 0, 0, COMP, "U642D", TGSI_OPCODE_U642D } +}; + +const struct tgsi_opcode_info * +tgsi_get_opcode_info( uint opcode ) +{ + static boolean firsttime = 1; + + if (firsttime) { + unsigned i; + firsttime = 0; + for (i = 0; i < Elements(opcode_info); i++) + assert(opcode_info[i].opcode == i); + } + + if (opcode < TGSI_OPCODE_LAST) + return &opcode_info[opcode]; + + assert( 0 ); + return NULL; +} + + +const char * +tgsi_get_opcode_name( uint opcode ) +{ + const struct tgsi_opcode_info *info = tgsi_get_opcode_info(opcode); + return info->mnemonic; +} + + +const char * +tgsi_get_processor_name( uint processor ) +{ + switch (processor) { + case TGSI_PROCESSOR_VERTEX: + return "vertex shader"; + case TGSI_PROCESSOR_FRAGMENT: + return "fragment shader"; + case TGSI_PROCESSOR_GEOMETRY: + return "geometry shader"; + case TGSI_PROCESSOR_TESS_CTRL: + return "tessellation control shader"; + case TGSI_PROCESSOR_TESS_EVAL: + return "tessellation evaluation shader"; + default: + return "unknown shader type!"; + } +} + +/** + * Infer the type (of the dst) of the opcode. + * + * MOV and UCMP is special so return VOID + */ +static inline enum tgsi_opcode_type +tgsi_opcode_infer_type( uint opcode ) +{ + switch (opcode) { + case TGSI_OPCODE_MOV: + case TGSI_OPCODE_UCMP: + return TGSI_TYPE_UNTYPED; + case TGSI_OPCODE_NOT: + case TGSI_OPCODE_SHL: + case TGSI_OPCODE_AND: + case TGSI_OPCODE_OR: + case TGSI_OPCODE_XOR: + case TGSI_OPCODE_TXQ: + case TGSI_OPCODE_TXQS: + case TGSI_OPCODE_F2U: + case TGSI_OPCODE_UDIV: + case TGSI_OPCODE_UMAD: + case TGSI_OPCODE_UMAX: + case TGSI_OPCODE_UMIN: + case TGSI_OPCODE_UMOD: + case TGSI_OPCODE_UMUL: + case TGSI_OPCODE_USEQ: + case TGSI_OPCODE_USGE: + case TGSI_OPCODE_USHR: + case TGSI_OPCODE_USLT: + case TGSI_OPCODE_USNE: + case TGSI_OPCODE_SVIEWINFO: + case TGSI_OPCODE_UMUL_HI: + case TGSI_OPCODE_UBFE: + case TGSI_OPCODE_BFI: + case TGSI_OPCODE_BREV: + case TGSI_OPCODE_D2U: + case TGSI_OPCODE_CLOCK: + return TGSI_TYPE_UNSIGNED; + case TGSI_OPCODE_ARL: + case TGSI_OPCODE_ARR: + case TGSI_OPCODE_MOD: + case TGSI_OPCODE_F2I: + case TGSI_OPCODE_FSEQ: + case TGSI_OPCODE_FSGE: + case TGSI_OPCODE_FSLT: + case TGSI_OPCODE_FSNE: + case TGSI_OPCODE_IDIV: + case TGSI_OPCODE_IMAX: + case TGSI_OPCODE_IMIN: + case TGSI_OPCODE_INEG: + case TGSI_OPCODE_ISGE: + case TGSI_OPCODE_ISHR: + case TGSI_OPCODE_ISLT: + case TGSI_OPCODE_UADD: + case TGSI_OPCODE_UARL: + case TGSI_OPCODE_IABS: + case TGSI_OPCODE_ISSG: + case TGSI_OPCODE_IMUL_HI: + case TGSI_OPCODE_IBFE: + case TGSI_OPCODE_IMSB: + case TGSI_OPCODE_DSEQ: + case TGSI_OPCODE_DSGE: + case TGSI_OPCODE_DSLT: + case TGSI_OPCODE_DSNE: + case TGSI_OPCODE_D2I: + case TGSI_OPCODE_LSB: + case TGSI_OPCODE_POPC: + case TGSI_OPCODE_UMSB: + case TGSI_OPCODE_U64SEQ: + case TGSI_OPCODE_U64SNE: + case TGSI_OPCODE_U64SLT: + case TGSI_OPCODE_U64SGE: + case TGSI_OPCODE_I64SLT: + case TGSI_OPCODE_I64SGE: + return TGSI_TYPE_SIGNED; + case TGSI_OPCODE_DADD: + case TGSI_OPCODE_DABS: + case TGSI_OPCODE_DFMA: + case TGSI_OPCODE_DNEG: + case TGSI_OPCODE_DMUL: + case TGSI_OPCODE_DMAX: + case TGSI_OPCODE_DMIN: + case TGSI_OPCODE_DRCP: + case TGSI_OPCODE_DSQRT: + case TGSI_OPCODE_DMAD: + case TGSI_OPCODE_DLDEXP: + case TGSI_OPCODE_DFRACEXP: + case TGSI_OPCODE_DFRAC: + case TGSI_OPCODE_DRSQ: + case TGSI_OPCODE_DTRUNC: + case TGSI_OPCODE_DCEIL: + case TGSI_OPCODE_DFLR: + case TGSI_OPCODE_DROUND: + case TGSI_OPCODE_DSSG: + case TGSI_OPCODE_DDIV: + case TGSI_OPCODE_F2D: + case TGSI_OPCODE_I2D: + case TGSI_OPCODE_U2D: + case TGSI_OPCODE_U642D: + case TGSI_OPCODE_I642D: + return TGSI_TYPE_DOUBLE; + case TGSI_OPCODE_U64MAX: + case TGSI_OPCODE_U64MIN: + case TGSI_OPCODE_U64ADD: + case TGSI_OPCODE_U64MUL: + case TGSI_OPCODE_U64DIV: + case TGSI_OPCODE_U64MOD: + case TGSI_OPCODE_U64SHL: + case TGSI_OPCODE_U64SHR: + case TGSI_OPCODE_F2U64: + case TGSI_OPCODE_D2U64: + return TGSI_TYPE_UNSIGNED64; + case TGSI_OPCODE_I64MAX: + case TGSI_OPCODE_I64MIN: + case TGSI_OPCODE_I64ABS: + case TGSI_OPCODE_I64SSG: + case TGSI_OPCODE_I64NEG: + case TGSI_OPCODE_I64SHR: + case TGSI_OPCODE_I64DIV: + case TGSI_OPCODE_I64MOD: + case TGSI_OPCODE_F2I64: + case TGSI_OPCODE_U2I64: + case TGSI_OPCODE_I2I64: + case TGSI_OPCODE_D2I64: + return TGSI_TYPE_SIGNED64; + default: + return TGSI_TYPE_FLOAT; + } +} + +/* + * infer the source type of a TGSI opcode. + */ +enum tgsi_opcode_type +tgsi_opcode_infer_src_type( uint opcode ) +{ + switch (opcode) { + case TGSI_OPCODE_UIF: + case TGSI_OPCODE_TXF: + case TGSI_OPCODE_U2F: + case TGSI_OPCODE_U2D: + case TGSI_OPCODE_UADD: + case TGSI_OPCODE_SWITCH: + case TGSI_OPCODE_CASE: + case TGSI_OPCODE_SAMPLE_I: + case TGSI_OPCODE_SAMPLE_I_MS: + case TGSI_OPCODE_UMUL_HI: + case TGSI_OPCODE_UMSB: + case TGSI_OPCODE_U2I64: + case TGSI_OPCODE_MEMBAR: + return TGSI_TYPE_UNSIGNED; + case TGSI_OPCODE_IMUL_HI: + case TGSI_OPCODE_I2F: + case TGSI_OPCODE_I2D: + case TGSI_OPCODE_I2I64: + return TGSI_TYPE_SIGNED; + case TGSI_OPCODE_ARL: + case TGSI_OPCODE_ARR: + case TGSI_OPCODE_F2D: + case TGSI_OPCODE_F2I: + case TGSI_OPCODE_F2U: + case TGSI_OPCODE_FSEQ: + case TGSI_OPCODE_FSGE: + case TGSI_OPCODE_FSLT: + case TGSI_OPCODE_FSNE: + case TGSI_OPCODE_UCMP: + case TGSI_OPCODE_F2U64: + case TGSI_OPCODE_F2I64: + return TGSI_TYPE_FLOAT; + case TGSI_OPCODE_D2F: + case TGSI_OPCODE_D2U: + case TGSI_OPCODE_D2I: + case TGSI_OPCODE_DSEQ: + case TGSI_OPCODE_DSGE: + case TGSI_OPCODE_DSLT: + case TGSI_OPCODE_DSNE: + case TGSI_OPCODE_D2U64: + case TGSI_OPCODE_D2I64: + return TGSI_TYPE_DOUBLE; + case TGSI_OPCODE_U64SEQ: + case TGSI_OPCODE_U64SNE: + case TGSI_OPCODE_U64SLT: + case TGSI_OPCODE_U64SGE: + case TGSI_OPCODE_U642F: + case TGSI_OPCODE_U642D: + return TGSI_TYPE_UNSIGNED64; + case TGSI_OPCODE_I64SLT: + case TGSI_OPCODE_I64SGE: + case TGSI_OPCODE_I642F: + case TGSI_OPCODE_I642D: + return TGSI_TYPE_SIGNED64; + default: + return tgsi_opcode_infer_type(opcode); + } +} + +/* + * infer the destination type of a TGSI opcode. + */ +enum tgsi_opcode_type +tgsi_opcode_infer_dst_type( uint opcode ) +{ + return tgsi_opcode_infer_type(opcode); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_info.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_info.h new file mode 100644 index 0000000..5e4fa84 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_info.h @@ -0,0 +1,115 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_INFO_H +#define TGSI_INFO_H + +#include "pipe/p_compiler.h" +#include "pipe/p_shader_tokens.h" +#include "util/u_format.h" + +#if defined __cplusplus +extern "C" { +#endif + +/* This enum describes how an opcode calculates its result. */ +enum tgsi_output_mode { + /** The opcode produces no result. */ + TGSI_OUTPUT_NONE = 0, + + /** When this opcode writes to a channel of the destination register, + * it takes as arguments values from the same channel of the source + * register(s). + * + * Example: TGSI_OPCODE_ADD + */ + TGSI_OUTPUT_COMPONENTWISE = 1, + + /** This opcode writes the same value to all enabled channels of the + * destination register. + * + * Example: TGSI_OPCODE_RSQ + */ + TGSI_OUTPUT_REPLICATE = 2, + + /** The operation performed by this opcode is dependent on which channel + * of the destination register is being written. + * + * Example: TGSI_OPCODE_LOG + */ + TGSI_OUTPUT_CHAN_DEPENDENT = 3, + + /** + * Example: TGSI_OPCODE_TEX + */ + TGSI_OUTPUT_OTHER = 4 +}; + +struct tgsi_opcode_info +{ + unsigned num_dst:3; + unsigned num_src:3; + unsigned is_tex:1; + unsigned is_branch:1; + int pre_dedent:2; + int post_indent:2; + enum tgsi_output_mode output_mode:3; + const char *mnemonic; + uint opcode; +}; + +const struct tgsi_opcode_info * +tgsi_get_opcode_info( uint opcode ); + +const char * +tgsi_get_opcode_name( uint opcode ); + +const char * +tgsi_get_processor_name( uint processor ); + +enum tgsi_opcode_type { + TGSI_TYPE_UNTYPED, /* for MOV */ + TGSI_TYPE_VOID, + TGSI_TYPE_UNSIGNED, + TGSI_TYPE_SIGNED, + TGSI_TYPE_FLOAT, + TGSI_TYPE_DOUBLE, + TGSI_TYPE_UNSIGNED64, + TGSI_TYPE_SIGNED64 +}; + +enum tgsi_opcode_type +tgsi_opcode_infer_src_type( uint opcode ); + +enum tgsi_opcode_type +tgsi_opcode_infer_dst_type( uint opcode ); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_INFO_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_iterate.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_iterate.c new file mode 100644 index 0000000..ca31894 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_iterate.c @@ -0,0 +1,90 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "tgsi_iterate.h" + +boolean +tgsi_iterate_shader( + const struct tgsi_token *tokens, + struct tgsi_iterate_context *ctx ) +{ + struct tgsi_parse_context parse; + + if (tgsi_parse_init( &parse, tokens ) != TGSI_PARSE_OK) + return FALSE; + + ctx->processor = parse.FullHeader.Processor; + + if (ctx->prolog) + if (!ctx->prolog( ctx )) + goto fail; + + while (!tgsi_parse_end_of_tokens( &parse )) { + tgsi_parse_token( &parse ); + + switch (parse.FullToken.Token.Type) { + case TGSI_TOKEN_TYPE_INSTRUCTION: + if (ctx->iterate_instruction) + if (!ctx->iterate_instruction( ctx, &parse.FullToken.FullInstruction )) + goto fail; + break; + + case TGSI_TOKEN_TYPE_DECLARATION: + if (ctx->iterate_declaration) + if (!ctx->iterate_declaration( ctx, &parse.FullToken.FullDeclaration )) + goto fail; + break; + + case TGSI_TOKEN_TYPE_IMMEDIATE: + if (ctx->iterate_immediate) + if (!ctx->iterate_immediate( ctx, &parse.FullToken.FullImmediate )) + goto fail; + break; + + case TGSI_TOKEN_TYPE_PROPERTY: + if (ctx->iterate_property) + if (!ctx->iterate_property( ctx, &parse.FullToken.FullProperty )) + goto fail; + break; + + default: + assert( 0 ); + } + } + + if (ctx->epilog) + if (!ctx->epilog( ctx )) + goto fail; + + tgsi_parse_free( &parse ); + return TRUE; + +fail: + tgsi_parse_free( &parse ); + return FALSE; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_iterate.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_iterate.h new file mode 100644 index 0000000..d925a06 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_iterate.h @@ -0,0 +1,80 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_ITERATE_H +#define TGSI_ITERATE_H + +#include "pipe/p_shader_tokens.h" +#include "tgsi/tgsi_parse.h" + +#if defined __cplusplus +extern "C" { +#endif + +struct tgsi_iterate_context +{ + boolean + (* prolog)( + struct tgsi_iterate_context *ctx ); + + boolean + (* iterate_instruction)( + struct tgsi_iterate_context *ctx, + struct tgsi_full_instruction *inst ); + + boolean + (* iterate_declaration)( + struct tgsi_iterate_context *ctx, + struct tgsi_full_declaration *decl ); + + boolean + (* iterate_immediate)( + struct tgsi_iterate_context *ctx, + struct tgsi_full_immediate *imm ); + + boolean + (* iterate_property)( + struct tgsi_iterate_context *ctx, + struct tgsi_full_property *prop ); + + boolean + (* epilog)( + struct tgsi_iterate_context *ctx ); + + struct tgsi_processor processor; +}; + +boolean +tgsi_iterate_shader( + const struct tgsi_token *tokens, + struct tgsi_iterate_context *ctx ); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_ITERATE_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_opcode_tmp.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_opcode_tmp.h new file mode 100644 index 0000000..92aab15 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_opcode_tmp.h @@ -0,0 +1,218 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#ifndef OP12_TEX +#define OP12_TEX(a) OP12(a) +#endif + +#ifndef OP14_TEX +#define OP14_TEX(a) OP14(a) +#endif + +#ifndef OP12_SAMPLE +#define OP12_SAMPLE(a) OP12(a) +#endif + +#ifndef OP13_SAMPLE +#define OP13_SAMPLE(a) OP13(a) +#endif + +#ifndef OP14_SAMPLE +#define OP14_SAMPLE(a) OP14(a) +#endif + +#ifndef OP15_SAMPLE +#define OP15_SAMPLE(a) OP15(a) +#endif + +#ifndef OP00_LBL +#define OP00_LBL(a) OP00(a) +#endif + +#ifndef OP01_LBL +#define OP01_LBL(a) OP01(a) +#endif + +OP11(ARL) +OP11(MOV) +OP11(LIT) +OP11(RCP) +OP11(RSQ) +OP11(EXP) +OP11(LOG) +OP12(MUL) +OP12(ADD) +OP12(DP3) +OP12(DP4) +OP12(DST) +OP12(MIN) +OP12(MAX) +OP12(SLT) +OP12(SGE) +OP13(MAD) +OP12(SUB) +OP13(LRP) +OP11(SQRT) +OP11(FRC) +OP11(FLR) +OP11(ROUND) +OP11(EX2) +OP11(LG2) +OP12(POW) +OP12(XPD) +OP11(ABS) +OP12(DPH) +OP11(COS) +OP11(DDX) +OP11(DDY) +OP00(KILL) +OP11(PK2H) +OP11(PK2US) +OP11(PK4B) +OP11(PK4UB) +OP12(SEQ) +OP12(SGT) +OP11(SIN) +OP12(SLE) +OP12(SNE) +OP12_TEX(TEX) +OP14_TEX(TXD) +OP12_TEX(TXP) +OP11(UP2H) +OP11(UP2US) +OP11(UP4B) +OP11(UP4UB) +OP11(ARR) +OP00_LBL(CAL) +OP00(RET) +OP11(SSG) +OP13(CMP) +OP11(SCS) +OP12_TEX(TXB) +OP12(DIV) +OP12(DP2) +OP12_TEX(TXL) +OP00(BRK) +OP01_LBL(IF) +OP01_LBL(UIF) +OP00_LBL(ELSE) +OP00(ENDIF) +OP11(CEIL) +OP11(I2F) +OP11(NOT) +OP11(TRUNC) +OP12(SHL) +OP12(AND) +OP12(OR) +OP12(MOD) +OP12(XOR) +OP12_TEX(TXF) +OP12_TEX(TXQ) +OP00(CONT) +OP01(EMIT) +OP01(ENDPRIM) +OP00_LBL(BGNLOOP) +OP00(BGNSUB) +OP00_LBL(ENDLOOP) +OP00(ENDSUB) +OP00(NOP) +OP01(KILL_IF) +OP00(END) +OP11(F2I) +OP12(FSEQ) +OP12(FSGE) +OP12(FSLT) +OP12(FSNE) +OP12(IDIV) +OP12(IMAX) +OP12(IMIN) +OP11(INEG) +OP12(ISGE) +OP12(ISHR) +OP12(ISLT) +OP11(F2U) +OP11(U2F) +OP12(UADD) +OP12(UDIV) +OP13(UMAD) +OP12(UMAX) +OP12(UMIN) +OP12(UMOD) +OP12(UMUL) +OP12(USEQ) +OP12(USGE) +OP12(USHR) +OP12(USLT) +OP12(USNE) +OP01(SWITCH) +OP01(CASE) +OP00(DEFAULT) +OP00(ENDSWITCH) + +OP13_SAMPLE(SAMPLE) +OP12_SAMPLE(SAMPLE_I) +OP13_SAMPLE(SAMPLE_I_MS) +OP14_SAMPLE(SAMPLE_B) +OP14_SAMPLE(SAMPLE_C) +OP14_SAMPLE(SAMPLE_C_LZ) +OP15_SAMPLE(SAMPLE_D) +OP14_SAMPLE(SAMPLE_L) +OP13_SAMPLE(GATHER4) +OP12(SVIEWINFO) +OP13(SAMPLE_POS) +OP12(SAMPLE_INFO) +OP11(UARL) + +OP13(UCMP) + +OP12(IMUL_HI) +OP12(UMUL_HI) + +#undef OP00 +#undef OP01 +#undef OP10 +#undef OP11 +#undef OP12 +#undef OP13 + +#ifdef OP14 +#undef OP14 +#endif + +#ifdef OP15 +#undef OP15 +#endif + +#undef OP00_LBL +#undef OP01_LBL + +#undef OP12_TEX +#undef OP14_TEX + +#undef OP12_SAMPLE +#undef OP13_SAMPLE +#undef OP14_SAMPLE +#undef OP15_SAMPLE diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_parse.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_parse.c new file mode 100644 index 0000000..58f31b3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_parse.c @@ -0,0 +1,312 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "pipe/p_shader_tokens.h" +#include "tgsi_parse.h" +#include "util/u_memory.h" + +unsigned +tgsi_parse_init( + struct tgsi_parse_context *ctx, + const struct tgsi_token *tokens ) +{ + ctx->FullHeader.Header = *(struct tgsi_header *) &tokens[0]; + if( ctx->FullHeader.Header.HeaderSize >= 2 ) { + ctx->FullHeader.Processor = *(struct tgsi_processor *) &tokens[1]; + } + else { + return TGSI_PARSE_ERROR; + } + + ctx->Tokens = tokens; + ctx->Position = ctx->FullHeader.Header.HeaderSize; + + return TGSI_PARSE_OK; +} + +void +tgsi_parse_free( + UNUSED struct tgsi_parse_context *ctx ) +{ +} + +boolean +tgsi_parse_end_of_tokens( + struct tgsi_parse_context *ctx ) +{ + /* All values involved are unsigned, but the sum will be promoted to + * a signed value (at least on 64 bit). To capture a possible overflow + * make it a signed comparison. + */ + return (int)ctx->Position >= + ctx->FullHeader.Header.HeaderSize + ctx->FullHeader.Header.BodySize; +} + + +/** + * This function is used to avoid and work-around type punning/aliasing + * warnings. The warnings seem harmless on x86 but on PPC they cause + * real failures. + */ +static inline void +copy_token(void *dst, const void *src) +{ + memcpy(dst, src, 4); +} + + +/** + * Get next 4-byte token, return it at address specified by 'token' + */ +static void +next_token( + struct tgsi_parse_context *ctx, + void *token ) +{ + assert( !tgsi_parse_end_of_tokens( ctx ) ); + copy_token(token, &ctx->Tokens[ctx->Position]); + ctx->Position++; +} + + +void +tgsi_parse_token( + struct tgsi_parse_context *ctx ) +{ + struct tgsi_token token; + unsigned i; + + next_token( ctx, &token ); + + switch( token.Type ) { + case TGSI_TOKEN_TYPE_DECLARATION: + { + struct tgsi_full_declaration *decl = &ctx->FullToken.FullDeclaration; + + memset(decl, 0, sizeof *decl); + copy_token(&decl->Declaration, &token); + + next_token( ctx, &decl->Range ); + + if (decl->Declaration.Dimension) { + next_token(ctx, &decl->Dim); + } + + if( decl->Declaration.Interpolate ) { + next_token( ctx, &decl->Interp ); + } + + if( decl->Declaration.Semantic ) { + next_token( ctx, &decl->Semantic ); + } + + if (decl->Declaration.File == TGSI_FILE_IMAGE) { + next_token(ctx, &decl->Image); + } + + if (decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) { + next_token(ctx, &decl->SamplerView); + } + + if( decl->Declaration.Array ) { + next_token(ctx, &decl->Array); + } + + break; + } + + case TGSI_TOKEN_TYPE_IMMEDIATE: + { + struct tgsi_full_immediate *imm = &ctx->FullToken.FullImmediate; + uint imm_count; + + memset(imm, 0, sizeof *imm); + copy_token(&imm->Immediate, &token); + + imm_count = imm->Immediate.NrTokens - 1; + + switch (imm->Immediate.DataType) { + case TGSI_IMM_FLOAT32: + for (i = 0; i < imm_count; i++) { + next_token(ctx, &imm->u[i].Float); + } + break; + + case TGSI_IMM_UINT32: + case TGSI_IMM_FLOAT64: + for (i = 0; i < imm_count; i++) { + next_token(ctx, &imm->u[i].Uint); + } + break; + + case TGSI_IMM_INT32: + for (i = 0; i < imm_count; i++) { + next_token(ctx, &imm->u[i].Int); + } + break; + + default: + assert( 0 ); + } + + break; + } + + case TGSI_TOKEN_TYPE_INSTRUCTION: + { + struct tgsi_full_instruction *inst = &ctx->FullToken.FullInstruction; + + memset(inst, 0, sizeof *inst); + copy_token(&inst->Instruction, &token); + + if (inst->Instruction.Label) { + next_token( ctx, &inst->Label); + } + + if (inst->Instruction.Texture) { + next_token( ctx, &inst->Texture); + for( i = 0; i < inst->Texture.NumOffsets; i++ ) { + next_token( ctx, &inst->TexOffsets[i] ); + } + } + + if (inst->Instruction.Memory) { + next_token(ctx, &inst->Memory); + } + + assert( inst->Instruction.NumDstRegs <= TGSI_FULL_MAX_DST_REGISTERS ); + + for( i = 0; i < inst->Instruction.NumDstRegs; i++ ) { + + next_token( ctx, &inst->Dst[i].Register ); + + if( inst->Dst[i].Register.Indirect ) + next_token( ctx, &inst->Dst[i].Indirect ); + + if( inst->Dst[i].Register.Dimension ) { + next_token( ctx, &inst->Dst[i].Dimension ); + + /* + * No support for multi-dimensional addressing. + */ + assert( !inst->Dst[i].Dimension.Dimension ); + + if( inst->Dst[i].Dimension.Indirect ) + next_token( ctx, &inst->Dst[i].DimIndirect ); + } + } + + assert( inst->Instruction.NumSrcRegs <= TGSI_FULL_MAX_SRC_REGISTERS ); + + for( i = 0; i < inst->Instruction.NumSrcRegs; i++ ) { + + next_token( ctx, &inst->Src[i].Register ); + + if( inst->Src[i].Register.Indirect ) + next_token( ctx, &inst->Src[i].Indirect ); + + if( inst->Src[i].Register.Dimension ) { + next_token( ctx, &inst->Src[i].Dimension ); + + /* + * No support for multi-dimensional addressing. + */ + assert( !inst->Src[i].Dimension.Dimension ); + + if( inst->Src[i].Dimension.Indirect ) + next_token( ctx, &inst->Src[i].DimIndirect ); + } + } + + break; + } + + case TGSI_TOKEN_TYPE_PROPERTY: + { + struct tgsi_full_property *prop = &ctx->FullToken.FullProperty; + uint prop_count; + + memset(prop, 0, sizeof *prop); + copy_token(&prop->Property, &token); + + prop_count = prop->Property.NrTokens - 1; + for (i = 0; i < prop_count; i++) { + next_token(ctx, &prop->u[i]); + } + + break; + } + + default: + assert( 0 ); + } +} + + + + +/** + * Make a new copy of a token array. + */ +struct tgsi_token * +tgsi_dup_tokens(const struct tgsi_token *tokens) +{ + unsigned n = tgsi_num_tokens(tokens); + unsigned bytes = n * sizeof(struct tgsi_token); + struct tgsi_token *new_tokens = (struct tgsi_token *) MALLOC(bytes); + if (new_tokens) + memcpy(new_tokens, tokens, bytes); + return new_tokens; +} + + +/** + * Allocate memory for num_tokens tokens. + */ +struct tgsi_token * +tgsi_alloc_tokens(unsigned num_tokens) +{ + unsigned bytes = num_tokens * sizeof(struct tgsi_token); + return (struct tgsi_token *) MALLOC(bytes); +} + + +void +tgsi_dump_tokens(const struct tgsi_token *tokens) +{ + const unsigned *dwords = (const unsigned *)tokens; + int nr = tgsi_num_tokens(tokens); + int i; + + assert(sizeof(*tokens) == sizeof(unsigned)); + + debug_printf("const unsigned tokens[%d] = {\n", nr); + for (i = 0; i < nr; i++) + debug_printf("0x%08x,\n", dwords[i]); + debug_printf("};\n"); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_parse.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_parse.h new file mode 100644 index 0000000..babd2b3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_parse.h @@ -0,0 +1,162 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_PARSE_H +#define TGSI_PARSE_H + +#include "pipe/p_compiler.h" +#include "pipe/p_shader_tokens.h" + +#if defined __cplusplus +extern "C" { +#endif + +struct tgsi_full_header +{ + struct tgsi_header Header; + struct tgsi_processor Processor; +}; + +struct tgsi_full_dst_register +{ + struct tgsi_dst_register Register; + struct tgsi_ind_register Indirect; + struct tgsi_dimension Dimension; + struct tgsi_ind_register DimIndirect; +}; + +struct tgsi_full_src_register +{ + struct tgsi_src_register Register; + struct tgsi_ind_register Indirect; + struct tgsi_dimension Dimension; + struct tgsi_ind_register DimIndirect; +}; + +struct tgsi_full_declaration +{ + struct tgsi_declaration Declaration; + struct tgsi_declaration_range Range; + struct tgsi_declaration_dimension Dim; + struct tgsi_declaration_interp Interp; + struct tgsi_declaration_semantic Semantic; + struct tgsi_declaration_image Image; + struct tgsi_declaration_sampler_view SamplerView; + struct tgsi_declaration_array Array; +}; + +struct tgsi_full_immediate +{ + struct tgsi_immediate Immediate; + union tgsi_immediate_data u[4]; +}; + +struct tgsi_full_property +{ + struct tgsi_property Property; + struct tgsi_property_data u[8]; +}; + +#define TGSI_FULL_MAX_DST_REGISTERS 2 +#define TGSI_FULL_MAX_SRC_REGISTERS 5 /* SAMPLE_D has 5 */ +#define TGSI_FULL_MAX_TEX_OFFSETS 4 + +struct tgsi_full_instruction +{ + struct tgsi_instruction Instruction; + struct tgsi_instruction_label Label; + struct tgsi_instruction_texture Texture; + struct tgsi_instruction_memory Memory; + struct tgsi_full_dst_register Dst[TGSI_FULL_MAX_DST_REGISTERS]; + struct tgsi_full_src_register Src[TGSI_FULL_MAX_SRC_REGISTERS]; + struct tgsi_texture_offset TexOffsets[TGSI_FULL_MAX_TEX_OFFSETS]; +}; + +union tgsi_full_token +{ + struct tgsi_token Token; + struct tgsi_full_declaration FullDeclaration; + struct tgsi_full_immediate FullImmediate; + struct tgsi_full_instruction FullInstruction; + struct tgsi_full_property FullProperty; +}; + +struct tgsi_parse_context +{ + const struct tgsi_token *Tokens; + unsigned Position; + struct tgsi_full_header FullHeader; + union tgsi_full_token FullToken; +}; + +#define TGSI_PARSE_OK 0 +#define TGSI_PARSE_ERROR 1 + +unsigned +tgsi_parse_init( + struct tgsi_parse_context *ctx, + const struct tgsi_token *tokens ); + +void +tgsi_parse_free( + struct tgsi_parse_context *ctx ); + +boolean +tgsi_parse_end_of_tokens( + struct tgsi_parse_context *ctx ); + +void +tgsi_parse_token( + struct tgsi_parse_context *ctx ); + +static inline unsigned +tgsi_num_tokens(const struct tgsi_token *tokens) +{ + struct tgsi_header header; + memcpy(&header, tokens, sizeof(header)); + return header.HeaderSize + header.BodySize; +} + +void +tgsi_dump_tokens(const struct tgsi_token *tokens); + +struct tgsi_token * +tgsi_dup_tokens(const struct tgsi_token *tokens); + +struct tgsi_token * +tgsi_alloc_tokens(unsigned num_tokens); + +void +tgsi_free_tokens(const struct tgsi_token *tokens); + + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_PARSE_H */ + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_sanity.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_sanity.c new file mode 100644 index 0000000..b44f458 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_sanity.c @@ -0,0 +1,565 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "util/u_memory.h" +#include "util/u_prim.h" +#include "cso_cache/cso_hash.h" +#include "tgsi_sanity.h" +#include "tgsi_info.h" +#include "tgsi_iterate.h" + + +DEBUG_GET_ONCE_BOOL_OPTION(print_sanity, "TGSI_PRINT_SANITY", FALSE) + + +typedef struct { + uint file : 28; + /* max 2 dimensions */ + uint dimensions : 4; + uint indices[2]; +} scan_register; + +struct sanity_check_ctx +{ + struct tgsi_iterate_context iter; + struct cso_hash *regs_decl; + struct cso_hash *regs_used; + struct cso_hash *regs_ind_used; + + uint num_imms; + uint num_instructions; + uint index_of_END; + + uint errors; + uint warnings; + uint implied_array_size; + + boolean print; +}; + +static inline unsigned +scan_register_key(const scan_register *reg) +{ + unsigned key = reg->file; + key |= (reg->indices[0] << 4); + key |= (reg->indices[1] << 18); + + return key; +} + +static void +fill_scan_register1d(scan_register *reg, + uint file, uint index) +{ + reg->file = file; + reg->dimensions = 1; + reg->indices[0] = index; + reg->indices[1] = 0; +} + +static void +fill_scan_register2d(scan_register *reg, + uint file, uint index1, uint index2) +{ + reg->file = file; + reg->dimensions = 2; + reg->indices[0] = index1; + reg->indices[1] = index2; +} + +static void +scan_register_dst(scan_register *reg, + struct tgsi_full_dst_register *dst) +{ + if (dst->Register.Dimension) { + /*FIXME: right now we don't support indirect + * multidimensional addressing */ + fill_scan_register2d(reg, + dst->Register.File, + dst->Register.Index, + dst->Dimension.Index); + } else { + fill_scan_register1d(reg, + dst->Register.File, + dst->Register.Index); + } +} + +static void +scan_register_src(scan_register *reg, + struct tgsi_full_src_register *src) +{ + if (src->Register.Dimension) { + /*FIXME: right now we don't support indirect + * multidimensional addressing */ + fill_scan_register2d(reg, + src->Register.File, + src->Register.Index, + src->Dimension.Index); + } else { + fill_scan_register1d(reg, + src->Register.File, + src->Register.Index); + } +} + +static scan_register * +create_scan_register_src(struct tgsi_full_src_register *src) +{ + scan_register *reg = MALLOC(sizeof(scan_register)); + scan_register_src(reg, src); + + return reg; +} + +static scan_register * +create_scan_register_dst(struct tgsi_full_dst_register *dst) +{ + scan_register *reg = MALLOC(sizeof(scan_register)); + scan_register_dst(reg, dst); + + return reg; +} + +static void +report_error( + struct sanity_check_ctx *ctx, + const char *format, + ... ) +{ + va_list args; + + if (!ctx->print) + return; + + debug_printf( "Error : " ); + va_start( args, format ); + _debug_vprintf( format, args ); + va_end( args ); + debug_printf( "\n" ); + ctx->errors++; +} + +static void +report_warning( + struct sanity_check_ctx *ctx, + const char *format, + ... ) +{ + va_list args; + + if (!ctx->print) + return; + + debug_printf( "Warning: " ); + va_start( args, format ); + _debug_vprintf( format, args ); + va_end( args ); + debug_printf( "\n" ); + ctx->warnings++; +} + +static boolean +check_file_name( + struct sanity_check_ctx *ctx, + uint file ) +{ + if (file <= TGSI_FILE_NULL || file >= TGSI_FILE_COUNT) { + report_error( ctx, "(%u): Invalid register file name", file ); + return FALSE; + } + return TRUE; +} + +static boolean +is_register_declared( + struct sanity_check_ctx *ctx, + const scan_register *reg) +{ + void *data = cso_hash_find_data_from_template( + ctx->regs_decl, scan_register_key(reg), + (void*)reg, sizeof(scan_register)); + return data ? TRUE : FALSE; +} + +static boolean +is_any_register_declared( + struct sanity_check_ctx *ctx, + uint file ) +{ + struct cso_hash_iter iter = + cso_hash_first_node(ctx->regs_decl); + + while (!cso_hash_iter_is_null(iter)) { + scan_register *reg = (scan_register *)cso_hash_iter_data(iter); + if (reg->file == file) + return TRUE; + iter = cso_hash_iter_next(iter); + } + + return FALSE; +} + +static boolean +is_register_used( + struct sanity_check_ctx *ctx, + scan_register *reg) +{ + void *data = cso_hash_find_data_from_template( + ctx->regs_used, scan_register_key(reg), + reg, sizeof(scan_register)); + return data ? TRUE : FALSE; +} + + +static boolean +is_ind_register_used( + struct sanity_check_ctx *ctx, + scan_register *reg) +{ + return cso_hash_contains(ctx->regs_ind_used, reg->file); +} + +static const char *file_names[TGSI_FILE_COUNT] = +{ + "NULL", + "CONST", + "IN", + "OUT", + "TEMP", + "SAMP", + "ADDR", + "IMM", + "PRED", + "SV", + "RES" +}; + +static boolean +check_register_usage( + struct sanity_check_ctx *ctx, + scan_register *reg, + const char *name, + boolean indirect_access ) +{ + if (!check_file_name( ctx, reg->file )) { + FREE(reg); + return FALSE; + } + + if (indirect_access) { + /* Note that 'index' is an offset relative to the value of the + * address register. No range checking done here.*/ + reg->indices[0] = 0; + reg->indices[1] = 0; + if (!is_any_register_declared( ctx, reg->file )) + report_error( ctx, "%s: Undeclared %s register", file_names[reg->file], name ); + if (!is_ind_register_used(ctx, reg)) + cso_hash_insert(ctx->regs_ind_used, reg->file, reg); + else + FREE(reg); + } + else { + if (!is_register_declared( ctx, reg )) { + if (reg->dimensions == 2) { + report_error( ctx, "%s[%d][%d]: Undeclared %s register", file_names[reg->file], + reg->indices[0], reg->indices[1], name ); + } + else { + report_error( ctx, "%s[%d]: Undeclared %s register", file_names[reg->file], + reg->indices[0], name ); + } + } + if (!is_register_used( ctx, reg )) + cso_hash_insert(ctx->regs_used, scan_register_key(reg), reg); + else + FREE(reg); + } + return TRUE; +} + +static boolean +iter_instruction( + struct tgsi_iterate_context *iter, + struct tgsi_full_instruction *inst ) +{ + struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; + const struct tgsi_opcode_info *info; + uint i; + + if (inst->Instruction.Opcode == TGSI_OPCODE_END) { + if (ctx->index_of_END != ~0u) { + report_error( ctx, "Too many END instructions" ); + } + ctx->index_of_END = ctx->num_instructions; + } + + info = tgsi_get_opcode_info( inst->Instruction.Opcode ); + if (info == NULL) { + report_error( ctx, "(%u): Invalid instruction opcode", inst->Instruction.Opcode ); + return TRUE; + } + + if (info->num_dst != inst->Instruction.NumDstRegs) { + report_error( ctx, "%s: Invalid number of destination operands, should be %u", info->mnemonic, info->num_dst ); + } + if (info->num_src != inst->Instruction.NumSrcRegs) { + report_error( ctx, "%s: Invalid number of source operands, should be %u", info->mnemonic, info->num_src ); + } + + /* Check destination and source registers' validity. + * Mark the registers as used. + */ + for (i = 0; i < inst->Instruction.NumDstRegs; i++) { + scan_register *reg = create_scan_register_dst(&inst->Dst[i]); + check_register_usage( + ctx, + reg, + "destination", + FALSE ); + if (!inst->Dst[i].Register.WriteMask) { + report_error(ctx, "Destination register has empty writemask"); + } + } + for (i = 0; i < inst->Instruction.NumSrcRegs; i++) { + scan_register *reg = create_scan_register_src(&inst->Src[i]); + check_register_usage( + ctx, + reg, + "source", + (boolean)inst->Src[i].Register.Indirect ); + if (inst->Src[i].Register.Indirect) { + scan_register *ind_reg = MALLOC(sizeof(scan_register)); + + fill_scan_register1d(ind_reg, + inst->Src[i].Indirect.File, + inst->Src[i].Indirect.Index); + check_register_usage( + ctx, + ind_reg, + "indirect", + FALSE ); + } + } + + ctx->num_instructions++; + + return TRUE; +} + +static void +check_and_declare(struct sanity_check_ctx *ctx, + scan_register *reg) +{ + if (is_register_declared( ctx, reg)) + report_error( ctx, "%s[%u]: The same register declared more than once", + file_names[reg->file], reg->indices[0] ); + cso_hash_insert(ctx->regs_decl, + scan_register_key(reg), + reg); +} + + +static boolean +iter_declaration( + struct tgsi_iterate_context *iter, + struct tgsi_full_declaration *decl ) +{ + struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; + uint file; + uint i; + + /* No declarations allowed after the first instruction. + */ + if (ctx->num_instructions > 0) + report_error( ctx, "Instruction expected but declaration found" ); + + /* Check registers' validity. + * Mark the registers as declared. + */ + file = decl->Declaration.File; + if (!check_file_name( ctx, file )) + return TRUE; + for (i = decl->Range.First; i <= decl->Range.Last; i++) { + /* declared TGSI_FILE_INPUT's for geometry processor + * have an implied second dimension */ + if (file == TGSI_FILE_INPUT && + ctx->iter.processor.Processor == TGSI_PROCESSOR_GEOMETRY) { + uint vert; + for (vert = 0; vert < ctx->implied_array_size; ++vert) { + scan_register *reg = MALLOC(sizeof(scan_register)); + fill_scan_register2d(reg, file, i, vert); + check_and_declare(ctx, reg); + } + } else { + scan_register *reg = MALLOC(sizeof(scan_register)); + if (decl->Declaration.Dimension) { + fill_scan_register2d(reg, file, i, decl->Dim.Index2D); + } else { + fill_scan_register1d(reg, file, i); + } + check_and_declare(ctx, reg); + } + } + + return TRUE; +} + +static boolean +iter_immediate( + struct tgsi_iterate_context *iter, + struct tgsi_full_immediate *imm ) +{ + struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; + scan_register *reg; + + /* No immediates allowed after the first instruction. + */ + if (ctx->num_instructions > 0) + report_error( ctx, "Instruction expected but immediate found" ); + + /* Mark the register as declared. + */ + reg = MALLOC(sizeof(scan_register)); + fill_scan_register1d(reg, TGSI_FILE_IMMEDIATE, ctx->num_imms); + cso_hash_insert(ctx->regs_decl, scan_register_key(reg), reg); + ctx->num_imms++; + + /* Check data type validity. + */ + if (imm->Immediate.DataType != TGSI_IMM_FLOAT32 && + imm->Immediate.DataType != TGSI_IMM_UINT32 && + imm->Immediate.DataType != TGSI_IMM_INT32 && + imm->Immediate.DataType != TGSI_IMM_FLOAT64) { + report_error( ctx, "(%u): Invalid immediate data type", imm->Immediate.DataType ); + return TRUE; + } + + return TRUE; +} + + +static boolean +iter_property( + struct tgsi_iterate_context *iter, + struct tgsi_full_property *prop ) +{ + struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; + + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY && + prop->Property.PropertyName == TGSI_PROPERTY_GS_INPUT_PRIM) { + ctx->implied_array_size = u_vertices_per_prim(prop->u[0].Data); + } + return TRUE; +} + +static boolean +epilog( + struct tgsi_iterate_context *iter ) +{ + struct sanity_check_ctx *ctx = (struct sanity_check_ctx *) iter; + + /* There must be an END instruction somewhere. + */ + if (ctx->index_of_END == ~0u) { + report_error( ctx, "Missing END instruction" ); + } + + /* Check if all declared registers were used. + */ + { + struct cso_hash_iter iter = + cso_hash_first_node(ctx->regs_decl); + + while (!cso_hash_iter_is_null(iter)) { + scan_register *reg = (scan_register *)cso_hash_iter_data(iter); + if (!is_register_used(ctx, reg) && !is_ind_register_used(ctx, reg)) { + report_warning( ctx, "%s[%u]: Register never used", + file_names[reg->file], reg->indices[0] ); + } + iter = cso_hash_iter_next(iter); + } + } + + /* Print totals, if any. + */ + if (ctx->errors || ctx->warnings) + debug_printf( "%u errors, %u warnings\n", ctx->errors, ctx->warnings ); + + return TRUE; +} + +static void +regs_hash_destroy(struct cso_hash *hash) +{ + struct cso_hash_iter iter = cso_hash_first_node(hash); + while (!cso_hash_iter_is_null(iter)) { + scan_register *reg = (scan_register *)cso_hash_iter_data(iter); + iter = cso_hash_erase(hash, iter); + assert(reg->file < TGSI_FILE_COUNT); + FREE(reg); + } + cso_hash_delete(hash); +} + +boolean +tgsi_sanity_check( + const struct tgsi_token *tokens ) +{ + struct sanity_check_ctx ctx; + boolean retval; + + ctx.iter.prolog = NULL; + ctx.iter.iterate_instruction = iter_instruction; + ctx.iter.iterate_declaration = iter_declaration; + ctx.iter.iterate_immediate = iter_immediate; + ctx.iter.iterate_property = iter_property; + ctx.iter.epilog = epilog; + + ctx.regs_decl = cso_hash_create(); + ctx.regs_used = cso_hash_create(); + ctx.regs_ind_used = cso_hash_create(); + + ctx.num_imms = 0; + ctx.num_instructions = 0; + ctx.index_of_END = ~0; + + ctx.errors = 0; + ctx.warnings = 0; + ctx.implied_array_size = 0; + ctx.print = debug_get_option_print_sanity(); + + retval = tgsi_iterate_shader( tokens, &ctx.iter ); + regs_hash_destroy(ctx.regs_decl); + regs_hash_destroy(ctx.regs_used); + regs_hash_destroy(ctx.regs_ind_used); + if (retval == FALSE) + return FALSE; + + return ctx.errors == 0; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_sanity.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_sanity.h new file mode 100644 index 0000000..1ff7874 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_sanity.h @@ -0,0 +1,52 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_SANITY_H +#define TGSI_SANITY_H + +#if defined __cplusplus +extern "C" { +#endif + +#include "pipe/p_compiler.h" + +struct tgsi_token; + +/* Check the given token stream for errors and common mistakes. + * Diagnostic messages are printed out to the debug output, and is + * controlled by the debug option TGSI_PRINT_SANITY (default false). + * Returns TRUE if there are no errors, even though there could be some warnings. + */ +boolean +tgsi_sanity_check( + const struct tgsi_token *tokens ); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_SANITY_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_scan.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_scan.c new file mode 100644 index 0000000..df7adb1 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_scan.c @@ -0,0 +1,551 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * Copyright 2008 VMware, Inc. All rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * TGSI program scan utility. + * Used to determine which registers and instructions are used by a shader. + * + * Authors: Brian Paul + */ + + +#include "util/u_debug.h" +#include "util/u_math.h" +#include "util/u_memory.h" +#include "util/u_prim.h" +#include "tgsi/tgsi_parse.h" +#include "tgsi/tgsi_util.h" +#include "tgsi/tgsi_scan.h" + + + + +/** + * Scan the given TGSI shader to collect information such as number of + * registers used, special instructions used, etc. + * \return info the result of the scan + */ +void +tgsi_scan_shader(const struct tgsi_token *tokens, + struct tgsi_shader_info *info) +{ + uint procType, i; + struct tgsi_parse_context parse; + unsigned current_depth = 0; + + memset(info, 0, sizeof(*info)); + for (i = 0; i < TGSI_FILE_COUNT; i++) + info->file_max[i] = -1; + for (i = 0; i < Elements(info->const_file_max); i++) + info->const_file_max[i] = -1; + info->properties[TGSI_PROPERTY_GS_INVOCATIONS] = 1; + + /** + ** Setup to begin parsing input shader + **/ + if (tgsi_parse_init( &parse, tokens ) != TGSI_PARSE_OK) { + debug_printf("tgsi_parse_init() failed in tgsi_scan_shader()!\n"); + return; + } + procType = parse.FullHeader.Processor.Processor; + assert(procType == TGSI_PROCESSOR_FRAGMENT || + procType == TGSI_PROCESSOR_VERTEX || + procType == TGSI_PROCESSOR_GEOMETRY || + procType == TGSI_PROCESSOR_TESS_CTRL || + procType == TGSI_PROCESSOR_TESS_EVAL || + procType == TGSI_PROCESSOR_COMPUTE); + info->processor = procType; + + + /** + ** Loop over incoming program tokens/instructions + */ + while( !tgsi_parse_end_of_tokens( &parse ) ) { + + info->num_tokens++; + + tgsi_parse_token( &parse ); + + switch( parse.FullToken.Token.Type ) { + case TGSI_TOKEN_TYPE_INSTRUCTION: + { + const struct tgsi_full_instruction *fullinst + = &parse.FullToken.FullInstruction; + uint i; + + assert(fullinst->Instruction.Opcode < TGSI_OPCODE_LAST); + info->opcode_count[fullinst->Instruction.Opcode]++; + + switch (fullinst->Instruction.Opcode) { + case TGSI_OPCODE_IF: + case TGSI_OPCODE_UIF: + case TGSI_OPCODE_BGNLOOP: + current_depth++; + info->max_depth = MAX2(info->max_depth, current_depth); + break; + case TGSI_OPCODE_ENDIF: + case TGSI_OPCODE_ENDLOOP: + current_depth--; + break; + default: + break; + } + + if (fullinst->Instruction.Opcode == TGSI_OPCODE_INTERP_CENTROID || + fullinst->Instruction.Opcode == TGSI_OPCODE_INTERP_OFFSET || + fullinst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE) { + const struct tgsi_full_src_register *src0 = &fullinst->Src[0]; + unsigned input; + + if (src0->Register.Indirect && src0->Indirect.ArrayID) + input = info->input_array_first[src0->Indirect.ArrayID]; + else + input = src0->Register.Index; + + /* For the INTERP opcodes, the interpolation is always + * PERSPECTIVE unless LINEAR is specified. + */ + switch (info->input_interpolate[input]) { + case TGSI_INTERPOLATE_COLOR: + case TGSI_INTERPOLATE_CONSTANT: + case TGSI_INTERPOLATE_PERSPECTIVE: + switch (fullinst->Instruction.Opcode) { + case TGSI_OPCODE_INTERP_CENTROID: + info->uses_persp_opcode_interp_centroid = true; + break; + case TGSI_OPCODE_INTERP_OFFSET: + info->uses_persp_opcode_interp_offset = true; + break; + case TGSI_OPCODE_INTERP_SAMPLE: + info->uses_persp_opcode_interp_sample = true; + break; + } + break; + + case TGSI_INTERPOLATE_LINEAR: + switch (fullinst->Instruction.Opcode) { + case TGSI_OPCODE_INTERP_CENTROID: + info->uses_linear_opcode_interp_centroid = true; + break; + case TGSI_OPCODE_INTERP_OFFSET: + info->uses_linear_opcode_interp_offset = true; + break; + case TGSI_OPCODE_INTERP_SAMPLE: + info->uses_linear_opcode_interp_sample = true; + break; + } + break; + } + } + + if (fullinst->Instruction.Opcode >= TGSI_OPCODE_F2D && + fullinst->Instruction.Opcode <= TGSI_OPCODE_DSSG) + info->uses_doubles = true; + + for (i = 0; i < fullinst->Instruction.NumSrcRegs; i++) { + const struct tgsi_full_src_register *src = + &fullinst->Src[i]; + int ind = src->Register.Index; + + /* Mark which inputs are effectively used */ + if (src->Register.File == TGSI_FILE_INPUT) { + unsigned usage_mask; + usage_mask = tgsi_util_get_inst_usage_mask(fullinst, i); + if (src->Register.Indirect) { + for (ind = 0; ind < info->num_inputs; ++ind) { + info->input_usage_mask[ind] |= usage_mask; + } + } else { + assert(ind >= 0); + assert(ind < PIPE_MAX_SHADER_INPUTS); + info->input_usage_mask[ind] |= usage_mask; + } + + if (procType == TGSI_PROCESSOR_FRAGMENT && + info->reads_position && + src->Register.Index == 0 && + (src->Register.SwizzleX == TGSI_SWIZZLE_Z || + src->Register.SwizzleY == TGSI_SWIZZLE_Z || + src->Register.SwizzleZ == TGSI_SWIZZLE_Z || + src->Register.SwizzleW == TGSI_SWIZZLE_Z)) { + info->reads_z = TRUE; + } + } + + /* check for indirect register reads */ + if (src->Register.Indirect) { + info->indirect_files |= (1 << src->Register.File); + info->indirect_files_read |= (1 << src->Register.File); + } + + if (src->Register.Dimension && src->Dimension.Indirect) { + info->dimension_indirect_files |= (1 << src->Register.File); + } + /* MSAA samplers */ + if (src->Register.File == TGSI_FILE_SAMPLER) { + assert(fullinst->Instruction.Texture); + assert((unsigned)src->Register.Index < Elements(info->is_msaa_sampler)); + + if (fullinst->Instruction.Texture && + (fullinst->Texture.Texture == TGSI_TEXTURE_2D_MSAA || + fullinst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY_MSAA)) { + info->is_msaa_sampler[src->Register.Index] = TRUE; + } + } + } + + /* check for indirect register writes */ + for (i = 0; i < fullinst->Instruction.NumDstRegs; i++) { + const struct tgsi_full_dst_register *dst = &fullinst->Dst[i]; + if (dst->Register.Indirect) { + info->indirect_files |= (1 << dst->Register.File); + info->indirect_files_written |= (1 << dst->Register.File); + } + if (dst->Register.Dimension && dst->Dimension.Indirect) + info->dimension_indirect_files |= (1 << dst->Register.File); + } + + info->num_instructions++; + } + break; + + case TGSI_TOKEN_TYPE_DECLARATION: + { + const struct tgsi_full_declaration *fulldecl + = &parse.FullToken.FullDeclaration; + const uint file = fulldecl->Declaration.File; + uint reg; + + if (fulldecl->Declaration.Array) { + unsigned array_id = fulldecl->Array.ArrayID; + + switch (file) { + case TGSI_FILE_INPUT: + assert(array_id < ARRAY_SIZE(info->input_array_first)); + info->input_array_first[array_id] = fulldecl->Range.First; + info->input_array_last[array_id] = fulldecl->Range.Last; + break; + case TGSI_FILE_OUTPUT: + assert(array_id < ARRAY_SIZE(info->output_array_first)); + info->output_array_first[array_id] = fulldecl->Range.First; + info->output_array_last[array_id] = fulldecl->Range.Last; + break; + } + info->array_max[file] = MAX2(info->array_max[file], array_id); + } + + for (reg = fulldecl->Range.First; + reg <= fulldecl->Range.Last; + reg++) { + unsigned semName = fulldecl->Semantic.Name; + unsigned semIndex = + fulldecl->Semantic.Index + (reg - fulldecl->Range.First); + + /* only first 32 regs will appear in this bitfield */ + info->file_mask[file] |= (1 << reg); + info->file_count[file]++; + info->file_max[file] = MAX2(info->file_max[file], (int)reg); + + if (file == TGSI_FILE_CONSTANT) { + int buffer = 0; + + if (fulldecl->Declaration.Dimension) + buffer = fulldecl->Dim.Index2D; + + info->const_file_max[buffer] = + MAX2(info->const_file_max[buffer], (int)reg); + } + else if (file == TGSI_FILE_INPUT) { + info->input_semantic_name[reg] = (ubyte) semName; + info->input_semantic_index[reg] = (ubyte) semIndex; + info->input_interpolate[reg] = (ubyte)fulldecl->Interp.Interpolate; + info->input_interpolate_loc[reg] = (ubyte)fulldecl->Interp.Location; + info->input_cylindrical_wrap[reg] = (ubyte)fulldecl->Interp.CylindricalWrap; + info->num_inputs++; + + /* Only interpolated varyings. Don't include POSITION. + * Don't include integer varyings, because they are not + * interpolated. + */ + if (semName == TGSI_SEMANTIC_GENERIC || + semName == TGSI_SEMANTIC_TEXCOORD || + semName == TGSI_SEMANTIC_COLOR || + semName == TGSI_SEMANTIC_BCOLOR || + semName == TGSI_SEMANTIC_FOG || + semName == TGSI_SEMANTIC_CLIPDIST || + semName == TGSI_SEMANTIC_CULLDIST) { + switch (fulldecl->Interp.Interpolate) { + case TGSI_INTERPOLATE_COLOR: + case TGSI_INTERPOLATE_PERSPECTIVE: + switch (fulldecl->Interp.Location) { + case TGSI_INTERPOLATE_LOC_CENTER: + info->uses_persp_center = true; + break; + case TGSI_INTERPOLATE_LOC_CENTROID: + info->uses_persp_centroid = true; + break; + case TGSI_INTERPOLATE_LOC_SAMPLE: + info->uses_persp_sample = true; + break; + } + break; + case TGSI_INTERPOLATE_LINEAR: + switch (fulldecl->Interp.Location) { + case TGSI_INTERPOLATE_LOC_CENTER: + info->uses_linear_center = true; + break; + case TGSI_INTERPOLATE_LOC_CENTROID: + info->uses_linear_centroid = true; + break; + case TGSI_INTERPOLATE_LOC_SAMPLE: + info->uses_linear_sample = true; + break; + } + break; + /* TGSI_INTERPOLATE_CONSTANT doesn't do any interpolation. */ + } + } + + if (semName == TGSI_SEMANTIC_PRIMID) + info->uses_primid = TRUE; + else if (procType == TGSI_PROCESSOR_FRAGMENT) { + if (semName == TGSI_SEMANTIC_POSITION) + info->reads_position = TRUE; + else if (semName == TGSI_SEMANTIC_FACE) + info->uses_frontface = TRUE; + } + } + else if (file == TGSI_FILE_SYSTEM_VALUE) { + unsigned index = fulldecl->Range.First; + + info->system_value_semantic_name[index] = semName; + info->num_system_values = MAX2(info->num_system_values, + index + 1); + + if (semName == TGSI_SEMANTIC_INSTANCEID) { + info->uses_instanceid = TRUE; + } + else if (semName == TGSI_SEMANTIC_VERTEXID) { + info->uses_vertexid = TRUE; + } + else if (semName == TGSI_SEMANTIC_VERTEXID_NOBASE) { + info->uses_vertexid_nobase = TRUE; + } + else if (semName == TGSI_SEMANTIC_BASEVERTEX) { + info->uses_basevertex = TRUE; + } + else if (semName == TGSI_SEMANTIC_PRIMID) { + info->uses_primid = TRUE; + } else if (semName == TGSI_SEMANTIC_INVOCATIONID) { + info->uses_invocationid = TRUE; + } + } + else if (file == TGSI_FILE_OUTPUT) { + info->output_semantic_name[reg] = (ubyte) semName; + info->output_semantic_index[reg] = (ubyte) semIndex; + info->num_outputs++; + + if (semName == TGSI_SEMANTIC_COLOR) + info->colors_written |= 1 << semIndex; + + if (procType == TGSI_PROCESSOR_VERTEX || + procType == TGSI_PROCESSOR_GEOMETRY || + procType == TGSI_PROCESSOR_TESS_CTRL || + procType == TGSI_PROCESSOR_TESS_EVAL) { + if (semName == TGSI_SEMANTIC_VIEWPORT_INDEX) { + info->writes_viewport_index = TRUE; + } + else if (semName == TGSI_SEMANTIC_LAYER) { + info->writes_layer = TRUE; + } + else if (semName == TGSI_SEMANTIC_PSIZE) { + info->writes_psize = TRUE; + } + else if (semName == TGSI_SEMANTIC_CLIPVERTEX) { + info->writes_clipvertex = TRUE; + } + } + + if (procType == TGSI_PROCESSOR_FRAGMENT) { + if (semName == TGSI_SEMANTIC_POSITION) { + info->writes_z = TRUE; + } + else if (semName == TGSI_SEMANTIC_STENCIL) { + info->writes_stencil = TRUE; + } + } + + if (procType == TGSI_PROCESSOR_VERTEX) { + if (semName == TGSI_SEMANTIC_EDGEFLAG) { + info->writes_edgeflag = TRUE; + } + } + } else if (file == TGSI_FILE_SAMPLER) { + info->samplers_declared |= 1 << reg; + } + } + } + break; + + case TGSI_TOKEN_TYPE_IMMEDIATE: + { + uint reg = info->immediate_count++; + uint file = TGSI_FILE_IMMEDIATE; + + info->file_mask[file] |= (1 << reg); + info->file_count[file]++; + info->file_max[file] = MAX2(info->file_max[file], (int)reg); + } + break; + + case TGSI_TOKEN_TYPE_PROPERTY: + { + const struct tgsi_full_property *fullprop + = &parse.FullToken.FullProperty; + unsigned name = fullprop->Property.PropertyName; + unsigned value = fullprop->u[0].Data; + + assert(name < Elements(info->properties)); + info->properties[name] = value; + + switch (name) { + case TGSI_PROPERTY_NUM_CLIPDIST_ENABLED: + info->num_written_clipdistance = value; + info->clipdist_writemask |= (1 << value) - 1; + break; + case TGSI_PROPERTY_NUM_CULLDIST_ENABLED: + info->num_written_culldistance = value; + info->culldist_writemask |= (1 << value) - 1; + break; + } + } + break; + + default: + assert( 0 ); + } + } + + info->uses_kill = (info->opcode_count[TGSI_OPCODE_KILL_IF] || + info->opcode_count[TGSI_OPCODE_KILL]); + + /* The dimensions of the IN decleration in geometry shader have + * to be deduced from the type of the input primitive. + */ + if (procType == TGSI_PROCESSOR_GEOMETRY) { + unsigned input_primitive = + info->properties[TGSI_PROPERTY_GS_INPUT_PRIM]; + int num_verts = u_vertices_per_prim(input_primitive); + int j; + info->file_count[TGSI_FILE_INPUT] = num_verts; + info->file_max[TGSI_FILE_INPUT] = + MAX2(info->file_max[TGSI_FILE_INPUT], num_verts - 1); + for (j = 0; j < num_verts; ++j) { + info->file_mask[TGSI_FILE_INPUT] |= (1 << j); + } + } + + tgsi_parse_free (&parse); +} + + + +/** + * Check if the given shader is a "passthrough" shader consisting of only + * MOV instructions of the form: MOV OUT[n], IN[n] + * + */ +boolean +tgsi_is_passthrough_shader(const struct tgsi_token *tokens) +{ + struct tgsi_parse_context parse; + + /** + ** Setup to begin parsing input shader + **/ + if (tgsi_parse_init(&parse, tokens) != TGSI_PARSE_OK) { + debug_printf("tgsi_parse_init() failed in tgsi_is_passthrough_shader()!\n"); + return FALSE; + } + + /** + ** Loop over incoming program tokens/instructions + */ + while (!tgsi_parse_end_of_tokens(&parse)) { + + tgsi_parse_token(&parse); + + switch (parse.FullToken.Token.Type) { + case TGSI_TOKEN_TYPE_INSTRUCTION: + { + struct tgsi_full_instruction *fullinst = + &parse.FullToken.FullInstruction; + const struct tgsi_full_src_register *src = + &fullinst->Src[0]; + const struct tgsi_full_dst_register *dst = + &fullinst->Dst[0]; + + /* Do a whole bunch of checks for a simple move */ + if (fullinst->Instruction.Opcode != TGSI_OPCODE_MOV || + (src->Register.File != TGSI_FILE_INPUT && + src->Register.File != TGSI_FILE_SYSTEM_VALUE) || + dst->Register.File != TGSI_FILE_OUTPUT || + src->Register.Index != dst->Register.Index || + + src->Register.Negate || + src->Register.Absolute || + + src->Register.SwizzleX != TGSI_SWIZZLE_X || + src->Register.SwizzleY != TGSI_SWIZZLE_Y || + src->Register.SwizzleZ != TGSI_SWIZZLE_Z || + src->Register.SwizzleW != TGSI_SWIZZLE_W || + + dst->Register.WriteMask != TGSI_WRITEMASK_XYZW) + { + tgsi_parse_free(&parse); + return FALSE; + } + } + break; + + case TGSI_TOKEN_TYPE_DECLARATION: + /* fall-through */ + case TGSI_TOKEN_TYPE_IMMEDIATE: + /* fall-through */ + case TGSI_TOKEN_TYPE_PROPERTY: + /* fall-through */ + default: + ; /* no-op */ + } + } + + tgsi_parse_free(&parse); + + /* if we get here, it's a pass-through shader */ + return TRUE; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_scan.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_scan.h new file mode 100644 index 0000000..0849add --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_scan.h @@ -0,0 +1,150 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_SCAN_H +#define TGSI_SCAN_H + + +#include "pipe/p_compiler.h" +#include "pipe/p_state.h" +#include "pipe/p_shader_tokens.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Shader summary info + */ +struct tgsi_shader_info +{ + uint num_tokens; + + ubyte num_inputs; + ubyte num_outputs; + ubyte input_semantic_name[PIPE_MAX_SHADER_INPUTS]; /**< TGSI_SEMANTIC_x */ + ubyte input_semantic_index[PIPE_MAX_SHADER_INPUTS]; + ubyte input_interpolate[PIPE_MAX_SHADER_INPUTS]; + ubyte input_interpolate_loc[PIPE_MAX_SHADER_INPUTS]; + ubyte input_usage_mask[PIPE_MAX_SHADER_INPUTS]; + ubyte input_cylindrical_wrap[PIPE_MAX_SHADER_INPUTS]; + ubyte output_semantic_name[PIPE_MAX_SHADER_OUTPUTS]; /**< TGSI_SEMANTIC_x */ + ubyte output_semantic_index[PIPE_MAX_SHADER_OUTPUTS]; + + ubyte num_system_values; + ubyte system_value_semantic_name[PIPE_MAX_SHADER_INPUTS]; + + ubyte processor; + + uint file_mask[TGSI_FILE_COUNT]; /**< bitmask of declared registers */ + uint file_count[TGSI_FILE_COUNT]; /**< number of declared registers */ + int file_max[TGSI_FILE_COUNT]; /**< highest index of declared registers */ + int const_file_max[PIPE_MAX_CONSTANT_BUFFERS]; + unsigned samplers_declared; /**< bitmask of declared samplers */ + + ubyte input_array_first[PIPE_MAX_SHADER_INPUTS]; + ubyte input_array_last[PIPE_MAX_SHADER_INPUTS]; + ubyte output_array_first[PIPE_MAX_SHADER_OUTPUTS]; + ubyte output_array_last[PIPE_MAX_SHADER_OUTPUTS]; + unsigned array_max[TGSI_FILE_COUNT]; /**< highest index array per register file */ + + uint immediate_count; /**< number of immediates declared */ + uint num_instructions; + + uint opcode_count[TGSI_OPCODE_LAST]; /**< opcode histogram */ + + ubyte colors_written; + boolean reads_position; /**< does fragment shader read position? */ + boolean reads_z; /**< does fragment shader read depth? */ + boolean writes_z; /**< does fragment shader write Z value? */ + boolean writes_stencil; /**< does fragment shader write stencil value? */ + boolean writes_edgeflag; /**< vertex shader outputs edgeflag */ + boolean uses_kill; /**< KILL or KILL_IF instruction used? */ + boolean uses_persp_center; + boolean uses_persp_centroid; + boolean uses_persp_sample; + boolean uses_linear_center; + boolean uses_linear_centroid; + boolean uses_linear_sample; + boolean uses_persp_opcode_interp_centroid; + boolean uses_persp_opcode_interp_offset; + boolean uses_persp_opcode_interp_sample; + boolean uses_linear_opcode_interp_centroid; + boolean uses_linear_opcode_interp_offset; + boolean uses_linear_opcode_interp_sample; + boolean uses_instanceid; + boolean uses_vertexid; + boolean uses_vertexid_nobase; + boolean uses_basevertex; + boolean uses_primid; + boolean uses_frontface; + boolean uses_invocationid; + boolean writes_psize; + boolean writes_clipvertex; + boolean writes_viewport_index; + boolean writes_layer; + boolean is_msaa_sampler[PIPE_MAX_SAMPLERS]; + boolean uses_doubles; /**< uses any of the double instructions */ + unsigned clipdist_writemask; + unsigned culldist_writemask; + unsigned num_written_culldistance; + unsigned num_written_clipdistance; + /** + * Bitmask indicating which register files are accessed with + * indirect addressing. The bits are (1 << TGSI_FILE_x), etc. + */ + unsigned indirect_files; + /** + * Bitmask indicating which register files are read / written with + * indirect addressing. The bits are (1 << TGSI_FILE_x). + */ + unsigned indirect_files_read; + unsigned indirect_files_written; + + unsigned dimension_indirect_files; + + unsigned properties[TGSI_PROPERTY_COUNT]; /* index with TGSI_PROPERTY_ */ + + /** + * Max nesting limit of loops/if's + */ + unsigned max_depth; +}; + +extern void +tgsi_scan_shader(const struct tgsi_token *tokens, + struct tgsi_shader_info *info); + + +extern boolean +tgsi_is_passthrough_shader(const struct tgsi_token *tokens); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* TGSI_SCAN_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_strings.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_strings.c new file mode 100644 index 0000000..6e80011 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_strings.c @@ -0,0 +1,252 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * Copyright 2012 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#include "pipe/p_compiler.h" +#include "util/u_memory.h" +#include "tgsi_strings.h" + + +const char *tgsi_processor_type_names[6] = +{ + "FRAG", + "VERT", + "GEOM", + "TESS_CTRL", + "TESS_EVAL", + "COMP" +}; + +static const char *tgsi_file_names[] = +{ + "NULL", + "CONST", + "IN", + "OUT", + "TEMP", + "SAMP", + "ADDR", + "IMM", + "PRED", + "SV", + "IMAGE", + "SVIEW", + "BUFFER", + "MEMORY", + "HWATOMIC", +}; + +const char *tgsi_semantic_names[TGSI_SEMANTIC_COUNT] = +{ + "POSITION", + "COLOR", + "BCOLOR", + "FOG", + "PSIZE", + "GENERIC", + "NORMAL", + "FACE", + "EDGEFLAG", + "PRIM_ID", + "INSTANCEID", + "VERTEXID", + "STENCIL", + "CLIPDIST", + "CLIPVERTEX", + "GRID_SIZE", + "BLOCK_ID", + "BLOCK_SIZE", + "THREAD_ID", + "TEXCOORD", + "PCOORD", + "VIEWPORT_INDEX", + "LAYER", + "CULLDIST", + "SAMPLEID", + "SAMPLEPOS", + "SAMPLEMASK", + "INVOCATIONID", + "VERTEXID_NOBASE", + "BASEVERTEX", + "PATCH", + "TESSCOORD", + "TESSOUTER", + "TESSINNER", + "VERTICESIN", + "HELPER_INVOCATION", +}; + +const char *tgsi_texture_names[TGSI_TEXTURE_COUNT] = +{ + "BUFFER", + "1D", + "2D", + "3D", + "CUBE", + "RECT", + "SHADOW1D", + "SHADOW2D", + "SHADOWRECT", + "1D_ARRAY", + "2D_ARRAY", + "SHADOW1D_ARRAY", + "SHADOW2D_ARRAY", + "SHADOWCUBE", + "2D_MSAA", + "2D_ARRAY_MSAA", + "CUBEARRAY", + "SHADOWCUBEARRAY", + "UNKNOWN", +}; + +const char *tgsi_property_names[TGSI_PROPERTY_COUNT] = +{ + "GS_INPUT_PRIMITIVE", + "GS_OUTPUT_PRIMITIVE", + "GS_MAX_OUTPUT_VERTICES", + "FS_COORD_ORIGIN", + "FS_COORD_PIXEL_CENTER", + "FS_COLOR0_WRITES_ALL_CBUFS", + "FS_DEPTH_LAYOUT", + "VS_PROHIBIT_UCPS", + "GS_INVOCATIONS", + "VS_WINDOW_SPACE_POSITION", + "TCS_VERTICES_OUT", + "TES_PRIM_MODE", + "TES_SPACING", + "TES_VERTEX_ORDER_CW", + "TES_POINT_MODE", + "NUM_CLIPDIST_ENABLED", + "NUM_CULLDIST_ENABLED", + "FS_EARLY_DEPTH_STENCIL", + "FS_POST_DEPTH_COVERAGE", + "NEXT_SHADER", + "CS_FIXED_BLOCK_WIDTH", + "CS_FIXED_BLOCK_HEIGHT", + "CS_FIXED_BLOCK_DEPTH", + "MUL_ZERO_WINS", +}; + +const char *tgsi_return_type_names[TGSI_RETURN_TYPE_COUNT] = +{ + "UNORM", + "SNORM", + "SINT", + "UINT", + "FLOAT" +}; + +const char *tgsi_interpolate_names[TGSI_INTERPOLATE_COUNT] = +{ + "CONSTANT", + "LINEAR", + "PERSPECTIVE", + "COLOR" +}; + +const char *tgsi_interpolate_locations[TGSI_INTERPOLATE_LOC_COUNT] = +{ + "CENTER", + "CENTROID", + "SAMPLE", +}; + +const char *tgsi_invariant_name = "INVARIANT"; + +const char *tgsi_primitive_names[PIPE_PRIM_MAX] = +{ + "POINTS", + "LINES", + "LINE_LOOP", + "LINE_STRIP", + "TRIANGLES", + "TRIANGLE_STRIP", + "TRIANGLE_FAN", + "QUADS", + "QUAD_STRIP", + "POLYGON", + "LINES_ADJACENCY", + "LINE_STRIP_ADJACENCY", + "TRIANGLES_ADJACENCY", + "TRIANGLE_STRIP_ADJACENCY", + "PATCHES", +}; + +const char *tgsi_fs_coord_origin_names[2] = +{ + "UPPER_LEFT", + "LOWER_LEFT" +}; + +const char *tgsi_fs_coord_pixel_center_names[2] = +{ + "HALF_INTEGER", + "INTEGER" +}; + +const char *tgsi_immediate_type_names[4] = +{ + "FLT32", + "UINT32", + "INT32", + "FLT64" +}; + +const char *tgsi_memory_names[3] = +{ + "COHERENT", + "RESTRICT", + "VOLATILE", +}; + +static inline void +tgsi_strings_check(void) +{ + STATIC_ASSERT(Elements(tgsi_semantic_names) == TGSI_SEMANTIC_COUNT); + STATIC_ASSERT(Elements(tgsi_texture_names) == TGSI_TEXTURE_COUNT); + STATIC_ASSERT(Elements(tgsi_property_names) == TGSI_PROPERTY_COUNT); + STATIC_ASSERT(Elements(tgsi_primitive_names) == PIPE_PRIM_MAX); + STATIC_ASSERT(Elements(tgsi_interpolate_names) == TGSI_INTERPOLATE_COUNT); + STATIC_ASSERT(Elements(tgsi_return_type_names) == TGSI_RETURN_TYPE_COUNT); + (void) tgsi_processor_type_names; + (void) tgsi_return_type_names; + (void) tgsi_immediate_type_names; + (void) tgsi_fs_coord_origin_names; + (void) tgsi_fs_coord_pixel_center_names; +} + + +const char * +tgsi_file_name(unsigned file) +{ + STATIC_ASSERT(Elements(tgsi_file_names) == TGSI_FILE_COUNT); + if (file < Elements(tgsi_file_names)) + return tgsi_file_names[file]; + else + return "invalid file"; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_strings.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_strings.h new file mode 100644 index 0000000..bab883d --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_strings.h @@ -0,0 +1,76 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * Copyright 2012 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_STRINGS_H +#define TGSI_STRINGS_H + +#include "pipe/p_shader_tokens.h" +#include "pipe/p_state.h" + + +#if defined __cplusplus +extern "C" { +#endif + + +extern const char *tgsi_processor_type_names[6]; + +extern const char *tgsi_semantic_names[TGSI_SEMANTIC_COUNT]; + +extern const char *tgsi_texture_names[TGSI_TEXTURE_COUNT]; + +extern const char *tgsi_property_names[TGSI_PROPERTY_COUNT]; + +extern const char *tgsi_return_type_names[TGSI_RETURN_TYPE_COUNT]; + +extern const char *tgsi_interpolate_names[TGSI_INTERPOLATE_COUNT]; + +extern const char *tgsi_interpolate_locations[TGSI_INTERPOLATE_LOC_COUNT]; + +extern const char *tgsi_invariant_name; + +extern const char *tgsi_primitive_names[PIPE_PRIM_MAX]; + +extern const char *tgsi_fs_coord_origin_names[2]; + +extern const char *tgsi_fs_coord_pixel_center_names[2]; + +extern const char *tgsi_immediate_type_names[4]; + +extern const char *tgsi_memory_names[3]; + +const char * +tgsi_file_name(unsigned file); + + +#if defined __cplusplus +} +#endif + + +#endif /* TGSI_STRINGS_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_text.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_text.c new file mode 100644 index 0000000..080dbfa --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_text.c @@ -0,0 +1,1887 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "util/u_memory.h" +#include "util/u_prim.h" +#include "pipe/p_defines.h" +#include "util/u_inlines.h" +#include "tgsi_text.h" +#include "tgsi_build.h" +#include "tgsi_info.h" +#include "tgsi_parse.h" +#include "tgsi_sanity.h" +#include "tgsi_strings.h" +#include "tgsi_util.h" +#include "tgsi_dump.h" + +static boolean is_alpha_underscore( const char *cur ) +{ + return + (*cur >= 'a' && *cur <= 'z') || + (*cur >= 'A' && *cur <= 'Z') || + *cur == '_'; +} + +static boolean is_digit( const char *cur ) +{ + return *cur >= '0' && *cur <= '9'; +} + +static boolean is_digit_alpha_underscore( const char *cur ) +{ + return is_digit( cur ) || is_alpha_underscore( cur ); +} + +static char uprcase( char c ) +{ + if (c >= 'a' && c <= 'z') + return c + 'A' - 'a'; + return c; +} + +/* + * Ignore case of str1 and assume str1 is already uppercase. + * Return TRUE iff str1 and str2 are equal. + */ +static int +streq_nocase_uprcase(const char *str1, + const char *str2) +{ + while (*str1 && *str2) { + if (*str1 != uprcase(*str2)) + return FALSE; + str1++; + str2++; + } + return *str1 == 0 && *str2 == 0; +} + +/* Return TRUE if both strings match. + * The second string is terminated by zero. + * The pointer to the first string is moved at end of the read word + * on success. + */ +static boolean str_match_no_case( const char **pcur, const char *str ) +{ + const char *cur = *pcur; + + while (*str != '\0' && *str == uprcase( *cur )) { + str++; + cur++; + } + if (*str == '\0') { + *pcur = cur; + return TRUE; + } + return FALSE; +} + +/* Return TRUE if both strings match. + * The first string is be terminated by a non-digit non-letter non-underscore + * character, the second string is terminated by zero. + * The pointer to the first string is moved at end of the read word + * on success. + */ +static boolean str_match_nocase_whole( const char **pcur, const char *str ) +{ + const char *cur = *pcur; + + if (str_match_no_case(&cur, str) && + !is_digit_alpha_underscore(cur)) { + *pcur = cur; + return TRUE; + } + return FALSE; +} + +/* Return the array index that matches starting at *pcur, where the string at + * *pcur is terminated by a non-digit non-letter non-underscore. + * Returns -1 if no match is found. + * + * On success, the pointer to the first string is moved to the end of the read + * word. + */ +static int str_match_name_from_array(const char **pcur, + const char * const *array, + unsigned array_size) +{ + for (unsigned j = 0; j < array_size; ++j) { + if (str_match_nocase_whole(pcur, array[j])) + return j; + } + return -1; +} + +/* Return the format corresponding to the name at *pcur. + * Returns -1 if there is no format name. + * + * On success, the pointer to the string is moved to the end of the read format + * name. + */ +static int str_match_format(const char **pcur) +{ + for (unsigned i = 0; i < PIPE_FORMAT_COUNT; i++) { + const struct util_format_description *desc = + util_format_description(i); + if (desc && str_match_nocase_whole(pcur, desc->name)) { + return i; + } + } + return -1; +} + +/* Eat until eol + */ +static void eat_until_eol( const char **pcur ) +{ + while (**pcur != '\0' && **pcur != '\n') + (*pcur)++; +} + +/* Eat zero or more whitespaces. + */ +static void eat_opt_white( const char **pcur ) +{ + while (**pcur == ' ' || **pcur == '\t' || **pcur == '\n') + (*pcur)++; +} + +/* Eat one or more whitespaces. + * Return TRUE if at least one whitespace eaten. + */ +static boolean eat_white( const char **pcur ) +{ + const char *cur = *pcur; + + eat_opt_white( pcur ); + return *pcur > cur; +} + +/* Parse unsigned integer. + * No checks for overflow. + */ +static boolean parse_uint( const char **pcur, uint *val ) +{ + const char *cur = *pcur; + + if (is_digit( cur )) { + *val = *cur++ - '0'; + while (is_digit( cur )) + *val = *val * 10 + *cur++ - '0'; + *pcur = cur; + return TRUE; + } + return FALSE; +} + +static boolean parse_int( const char **pcur, int *val ) +{ + const char *cur = *pcur; + int sign = (*cur == '-' ? -1 : 1); + + if (*cur == '+' || *cur == '-') + cur++; + + if (parse_uint(&cur, (uint *)val)) { + *val *= sign; + *pcur = cur; + return TRUE; + } + + return FALSE; +} + +static boolean parse_identifier( const char **pcur, char *ret, size_t len ) +{ + const char *cur = *pcur; + size_t i = 0; + if (is_alpha_underscore( cur )) { + ret[i++] = *cur++; + while (is_alpha_underscore( cur ) || is_digit( cur )) { + if (i == len - 1) + return FALSE; + ret[i++] = *cur++; + } + ret[i++] = '\0'; + *pcur = cur; + return TRUE; + } + return FALSE; +} + +/* Parse floating point. + */ +static boolean parse_float( const char **pcur, float *val ) +{ + const char *cur = *pcur; + boolean integral_part = FALSE; + boolean fractional_part = FALSE; + + if (*cur == '0' && *(cur + 1) == 'x') { + union fi fi; + fi.ui = strtoul(cur, NULL, 16); + *val = fi.f; + cur += 10; + goto out; + } + + *val = (float) atof( cur ); + if (*cur == '-' || *cur == '+') + cur++; + if (is_digit( cur )) { + cur++; + integral_part = TRUE; + while (is_digit( cur )) + cur++; + } + if (*cur == '.') { + cur++; + if (is_digit( cur )) { + cur++; + fractional_part = TRUE; + while (is_digit( cur )) + cur++; + } + } + if (!integral_part && !fractional_part) + return FALSE; + if (uprcase( *cur ) == 'E') { + cur++; + if (*cur == '-' || *cur == '+') + cur++; + if (is_digit( cur )) { + cur++; + while (is_digit( cur )) + cur++; + } + else + return FALSE; + } + +out: + *pcur = cur; + return TRUE; +} + +static boolean parse_double( const char **pcur, uint32_t *val0, uint32_t *val1) +{ + const char *cur = *pcur; + union { + double dval; + uint32_t uval[2]; + } v; + + if (*cur == '0' && *(cur + 1) == 'x') { + *val0 = strtoul(cur, NULL, 16); + cur += 11; + *val1 = strtoul(cur, NULL, 16); + cur += 11; + *pcur = cur; + return TRUE; + } + v.dval = strtod(cur, (char**)pcur); + if (*pcur == cur) + return FALSE; + + *val0 = v.uval[0]; + *val1 = v.uval[1]; + + return TRUE; +} + +struct translate_ctx +{ + const char *text; + const char *cur; + struct tgsi_token *tokens; + struct tgsi_token *tokens_cur; + struct tgsi_token *tokens_end; + struct tgsi_header *header; + unsigned processor : 4; + unsigned implied_array_size : 6; + unsigned num_immediates; +}; + +static void report_error(struct translate_ctx *ctx, const char *format, ...) +{ + va_list args; + int line = 1; + int column = 1; + const char *itr = ctx->text; + + debug_printf("\nTGSI asm error: "); + + va_start(args, format); + _debug_vprintf(format, args); + va_end(args); + + while (itr != ctx->cur) { + if (*itr == '\n') { + column = 1; + ++line; + } + ++column; + ++itr; + } + + debug_printf(" [%d : %d] \n", line, column); +} + +/* Parse shader header. + * Return TRUE for one of the following headers. + * FRAG + * GEOM + * VERT + */ +static boolean parse_header( struct translate_ctx *ctx ) +{ + uint processor; + + if (str_match_nocase_whole( &ctx->cur, "FRAG" )) + processor = TGSI_PROCESSOR_FRAGMENT; + else if (str_match_nocase_whole( &ctx->cur, "VERT" )) + processor = TGSI_PROCESSOR_VERTEX; + else if (str_match_nocase_whole( &ctx->cur, "GEOM" )) + processor = TGSI_PROCESSOR_GEOMETRY; + else if (str_match_nocase_whole( &ctx->cur, "TESS_CTRL" )) + processor = TGSI_PROCESSOR_TESS_CTRL; + else if (str_match_nocase_whole( &ctx->cur, "TESS_EVAL" )) + processor = TGSI_PROCESSOR_TESS_EVAL; + else if (str_match_nocase_whole( &ctx->cur, "COMP" )) + processor = TGSI_PROCESSOR_COMPUTE; + else { + report_error( ctx, "Unknown header" ); + return FALSE; + } + + if (ctx->tokens_cur >= ctx->tokens_end) + return FALSE; + ctx->header = (struct tgsi_header *) ctx->tokens_cur++; + *ctx->header = tgsi_build_header(); + + if (ctx->tokens_cur >= ctx->tokens_end) + return FALSE; + *(struct tgsi_processor *) ctx->tokens_cur++ = tgsi_build_processor( processor, ctx->header ); + ctx->processor = processor; + + return TRUE; +} + +static boolean parse_label( struct translate_ctx *ctx, uint *val ) +{ + const char *cur = ctx->cur; + + if (parse_uint( &cur, val )) { + eat_opt_white( &cur ); + if (*cur == ':') { + cur++; + ctx->cur = cur; + return TRUE; + } + } + return FALSE; +} + +static boolean +parse_file( const char **pcur, uint *file ) +{ + uint i; + + for (i = 0; i < TGSI_FILE_COUNT; i++) { + const char *cur = *pcur; + + if (str_match_nocase_whole( &cur, tgsi_file_name(i) )) { + *pcur = cur; + *file = i; + return TRUE; + } + } + return FALSE; +} + +static boolean +parse_opt_writemask( + struct translate_ctx *ctx, + uint *writemask ) +{ + const char *cur; + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == '.') { + cur++; + *writemask = TGSI_WRITEMASK_NONE; + eat_opt_white( &cur ); + if (uprcase( *cur ) == 'X') { + cur++; + *writemask |= TGSI_WRITEMASK_X; + } + if (uprcase( *cur ) == 'Y') { + cur++; + *writemask |= TGSI_WRITEMASK_Y; + } + if (uprcase( *cur ) == 'Z') { + cur++; + *writemask |= TGSI_WRITEMASK_Z; + } + if (uprcase( *cur ) == 'W') { + cur++; + *writemask |= TGSI_WRITEMASK_W; + } + + if (*writemask == TGSI_WRITEMASK_NONE) { + report_error( ctx, "Writemask expected" ); + return FALSE; + } + + ctx->cur = cur; + } + else { + *writemask = TGSI_WRITEMASK_XYZW; + } + return TRUE; +} + + +/* ::= `[' + */ +static boolean +parse_register_file_bracket( + struct translate_ctx *ctx, + uint *file ) +{ + if (!parse_file( &ctx->cur, file )) { + report_error( ctx, "Unknown register file" ); + return FALSE; + } + eat_opt_white( &ctx->cur ); + if (*ctx->cur != '[') { + report_error( ctx, "Expected `['" ); + return FALSE; + } + ctx->cur++; + return TRUE; +} + +/* ::= + */ +static boolean +parse_register_file_bracket_index( + struct translate_ctx *ctx, + uint *file, + int *index ) +{ + uint uindex; + + if (!parse_register_file_bracket( ctx, file )) + return FALSE; + eat_opt_white( &ctx->cur ); + if (!parse_uint( &ctx->cur, &uindex )) { + report_error( ctx, "Expected literal unsigned integer" ); + return FALSE; + } + *index = (int) uindex; + return TRUE; +} + +/* Parse simple 1d register operand. + * ::= `]' + */ +static boolean +parse_register_1d(struct translate_ctx *ctx, + uint *file, + int *index ) +{ + if (!parse_register_file_bracket_index( ctx, file, index )) + return FALSE; + eat_opt_white( &ctx->cur ); + if (*ctx->cur != ']') { + report_error( ctx, "Expected `]'" ); + return FALSE; + } + ctx->cur++; + return TRUE; +} + +struct parsed_bracket { + int index; + + uint ind_file; + int ind_index; + uint ind_comp; + uint ind_array; +}; + + +static boolean +parse_register_bracket( + struct translate_ctx *ctx, + struct parsed_bracket *brackets) +{ + const char *cur; + uint uindex; + + memset(brackets, 0, sizeof(struct parsed_bracket)); + + eat_opt_white( &ctx->cur ); + + cur = ctx->cur; + if (parse_file( &cur, &brackets->ind_file )) { + if (!parse_register_1d( ctx, &brackets->ind_file, + &brackets->ind_index )) + return FALSE; + eat_opt_white( &ctx->cur ); + + if (*ctx->cur == '.') { + ctx->cur++; + eat_opt_white(&ctx->cur); + + switch (uprcase(*ctx->cur)) { + case 'X': + brackets->ind_comp = TGSI_SWIZZLE_X; + break; + case 'Y': + brackets->ind_comp = TGSI_SWIZZLE_Y; + break; + case 'Z': + brackets->ind_comp = TGSI_SWIZZLE_Z; + break; + case 'W': + brackets->ind_comp = TGSI_SWIZZLE_W; + break; + default: + report_error(ctx, "Expected indirect register swizzle component `x', `y', `z' or `w'"); + return FALSE; + } + ctx->cur++; + eat_opt_white(&ctx->cur); + } + + if (*ctx->cur == '+' || *ctx->cur == '-') + parse_int( &ctx->cur, &brackets->index ); + else + brackets->index = 0; + } + else { + if (!parse_uint( &ctx->cur, &uindex )) { + report_error( ctx, "Expected literal unsigned integer" ); + return FALSE; + } + brackets->index = (int) uindex; + brackets->ind_file = TGSI_FILE_NULL; + brackets->ind_index = 0; + } + eat_opt_white( &ctx->cur ); + if (*ctx->cur != ']') { + report_error( ctx, "Expected `]'" ); + return FALSE; + } + ctx->cur++; + if (*ctx->cur == '(') { + ctx->cur++; + eat_opt_white( &ctx->cur ); + if (!parse_uint( &ctx->cur, &brackets->ind_array )) { + report_error( ctx, "Expected literal unsigned integer" ); + return FALSE; + } + eat_opt_white( &ctx->cur ); + if (*ctx->cur != ')') { + report_error( ctx, "Expected `)'" ); + return FALSE; + } + ctx->cur++; + } + return TRUE; +} + +static boolean +parse_opt_register_src_bracket( + struct translate_ctx *ctx, + struct parsed_bracket *brackets, + int *parsed_brackets) +{ + const char *cur = ctx->cur; + + *parsed_brackets = 0; + + eat_opt_white( &cur ); + if (cur[0] == '[') { + ++cur; + ctx->cur = cur; + + if (!parse_register_bracket(ctx, brackets)) + return FALSE; + + *parsed_brackets = 1; + } + + return TRUE; +} + + +/* Parse source register operand. + * ::= `]' | + * [`.' (`x' | `y' | `z' | `w')] `]' | + * [`.' (`x' | `y' | `z' | `w')] `+' `]' | + * [`.' (`x' | `y' | `z' | `w')] `-' `]' + */ +static boolean +parse_register_src( + struct translate_ctx *ctx, + uint *file, + struct parsed_bracket *brackets) +{ + brackets->ind_comp = TGSI_SWIZZLE_X; + if (!parse_register_file_bracket( ctx, file )) + return FALSE; + if (!parse_register_bracket( ctx, brackets )) + return FALSE; + + return TRUE; +} + +struct parsed_dcl_bracket { + uint first; + uint last; +}; + +static boolean +parse_register_dcl_bracket( + struct translate_ctx *ctx, + struct parsed_dcl_bracket *bracket) +{ + uint uindex; + memset(bracket, 0, sizeof(struct parsed_dcl_bracket)); + + eat_opt_white( &ctx->cur ); + + if (!parse_uint( &ctx->cur, &uindex )) { + /* it can be an empty bracket [] which means its range + * is from 0 to some implied size */ + if (ctx->cur[0] == ']' && ctx->implied_array_size != 0) { + bracket->first = 0; + bracket->last = ctx->implied_array_size - 1; + goto cleanup; + } + report_error( ctx, "Expected literal unsigned integer" ); + return FALSE; + } + bracket->first = uindex; + + eat_opt_white( &ctx->cur ); + + if (ctx->cur[0] == '.' && ctx->cur[1] == '.') { + uint uindex; + + ctx->cur += 2; + eat_opt_white( &ctx->cur ); + if (!parse_uint( &ctx->cur, &uindex )) { + report_error( ctx, "Expected literal integer" ); + return FALSE; + } + bracket->last = (int) uindex; + eat_opt_white( &ctx->cur ); + } + else { + bracket->last = bracket->first; + } + +cleanup: + if (*ctx->cur != ']') { + report_error( ctx, "Expected `]' or `..'" ); + return FALSE; + } + ctx->cur++; + return TRUE; +} + +/* Parse register declaration. + * ::= `]' | + * `..' `]' + */ +static boolean +parse_register_dcl( + struct translate_ctx *ctx, + uint *file, + struct parsed_dcl_bracket *brackets, + int *num_brackets) +{ + const char *cur; + + *num_brackets = 0; + + if (!parse_register_file_bracket( ctx, file )) + return FALSE; + if (!parse_register_dcl_bracket( ctx, &brackets[0] )) + return FALSE; + + *num_brackets = 1; + + cur = ctx->cur; + eat_opt_white( &cur ); + + if (cur[0] == '[') { + bool is_in = *file == TGSI_FILE_INPUT; + bool is_out = *file == TGSI_FILE_OUTPUT; + + ++cur; + ctx->cur = cur; + if (!parse_register_dcl_bracket( ctx, &brackets[1] )) + return FALSE; + /* for geometry shader we don't really care about + * the first brackets it's always the size of the + * input primitive. so we want to declare just + * the index relevant to the semantics which is in + * the second bracket */ + + /* tessellation has similar constraints to geometry shader */ + if ((ctx->processor == TGSI_PROCESSOR_GEOMETRY && is_in) || + (ctx->processor == TGSI_PROCESSOR_TESS_EVAL && is_in) || + (ctx->processor == TGSI_PROCESSOR_TESS_CTRL && (is_in || is_out))) { + brackets[0] = brackets[1]; + *num_brackets = 1; + } else { + *num_brackets = 2; + } + } + + return TRUE; +} + + +/* Parse destination register operand.*/ +static boolean +parse_register_dst( + struct translate_ctx *ctx, + uint *file, + struct parsed_bracket *brackets) +{ + brackets->ind_comp = TGSI_SWIZZLE_X; + if (!parse_register_file_bracket( ctx, file )) + return FALSE; + if (!parse_register_bracket( ctx, brackets )) + return FALSE; + + return TRUE; +} + +static boolean +parse_dst_operand( + struct translate_ctx *ctx, + struct tgsi_full_dst_register *dst ) +{ + uint file; + uint writemask; + const char *cur; + struct parsed_bracket bracket[2]; + int parsed_opt_brackets; + + if (!parse_register_dst( ctx, &file, &bracket[0] )) + return FALSE; + if (!parse_opt_register_src_bracket(ctx, &bracket[1], &parsed_opt_brackets)) + return FALSE; + + cur = ctx->cur; + eat_opt_white( &cur ); + + if (!parse_opt_writemask( ctx, &writemask )) + return FALSE; + + dst->Register.File = file; + if (parsed_opt_brackets) { + dst->Register.Dimension = 1; + dst->Dimension.Indirect = 0; + dst->Dimension.Dimension = 0; + dst->Dimension.Index = bracket[0].index; + + if (bracket[0].ind_file != TGSI_FILE_NULL) { + dst->Dimension.Indirect = 1; + dst->DimIndirect.File = bracket[0].ind_file; + dst->DimIndirect.Index = bracket[0].ind_index; + dst->DimIndirect.Swizzle = bracket[0].ind_comp; + dst->DimIndirect.ArrayID = bracket[0].ind_array; + } + bracket[0] = bracket[1]; + } + dst->Register.Index = bracket[0].index; + dst->Register.WriteMask = writemask; + if (bracket[0].ind_file != TGSI_FILE_NULL) { + dst->Register.Indirect = 1; + dst->Indirect.File = bracket[0].ind_file; + dst->Indirect.Index = bracket[0].ind_index; + dst->Indirect.Swizzle = bracket[0].ind_comp; + dst->Indirect.ArrayID = bracket[0].ind_array; + } + return TRUE; +} + +static boolean +parse_optional_swizzle( + struct translate_ctx *ctx, + uint *swizzle, + boolean *parsed_swizzle, + int components) +{ + const char *cur = ctx->cur; + + *parsed_swizzle = FALSE; + + eat_opt_white( &cur ); + if (*cur == '.') { + int i; + + cur++; + eat_opt_white( &cur ); + for (i = 0; i < components; i++) { + if (uprcase( *cur ) == 'X') + swizzle[i] = TGSI_SWIZZLE_X; + else if (uprcase( *cur ) == 'Y') + swizzle[i] = TGSI_SWIZZLE_Y; + else if (uprcase( *cur ) == 'Z') + swizzle[i] = TGSI_SWIZZLE_Z; + else if (uprcase( *cur ) == 'W') + swizzle[i] = TGSI_SWIZZLE_W; + else { + report_error( ctx, "Expected register swizzle component `x', `y', `z' or `w'" ); + return FALSE; + } + cur++; + } + *parsed_swizzle = TRUE; + ctx->cur = cur; + } + return TRUE; +} + +static boolean +parse_src_operand( + struct translate_ctx *ctx, + struct tgsi_full_src_register *src ) +{ + uint file; + uint swizzle[4]; + boolean parsed_swizzle; + struct parsed_bracket bracket[2]; + int parsed_opt_brackets; + + if (*ctx->cur == '-') { + ctx->cur++; + eat_opt_white( &ctx->cur ); + src->Register.Negate = 1; + } + + if (*ctx->cur == '|') { + ctx->cur++; + eat_opt_white( &ctx->cur ); + src->Register.Absolute = 1; + } + + if (!parse_register_src(ctx, &file, &bracket[0])) + return FALSE; + if (!parse_opt_register_src_bracket(ctx, &bracket[1], &parsed_opt_brackets)) + return FALSE; + + src->Register.File = file; + if (parsed_opt_brackets) { + src->Register.Dimension = 1; + src->Dimension.Indirect = 0; + src->Dimension.Dimension = 0; + src->Dimension.Index = bracket[0].index; + if (bracket[0].ind_file != TGSI_FILE_NULL) { + src->Dimension.Indirect = 1; + src->DimIndirect.File = bracket[0].ind_file; + src->DimIndirect.Index = bracket[0].ind_index; + src->DimIndirect.Swizzle = bracket[0].ind_comp; + src->DimIndirect.ArrayID = bracket[0].ind_array; + } + bracket[0] = bracket[1]; + } + src->Register.Index = bracket[0].index; + if (bracket[0].ind_file != TGSI_FILE_NULL) { + src->Register.Indirect = 1; + src->Indirect.File = bracket[0].ind_file; + src->Indirect.Index = bracket[0].ind_index; + src->Indirect.Swizzle = bracket[0].ind_comp; + src->Indirect.ArrayID = bracket[0].ind_array; + } + + /* Parse optional swizzle. + */ + if (parse_optional_swizzle( ctx, swizzle, &parsed_swizzle, 4 )) { + if (parsed_swizzle) { + src->Register.SwizzleX = swizzle[0]; + src->Register.SwizzleY = swizzle[1]; + src->Register.SwizzleZ = swizzle[2]; + src->Register.SwizzleW = swizzle[3]; + } + } + + if (src->Register.Absolute) { + eat_opt_white( &ctx->cur ); + if (*ctx->cur != '|') { + report_error( ctx, "Expected `|'" ); + return FALSE; + } + ctx->cur++; + } + + + return TRUE; +} + +static boolean +parse_texoffset_operand( + struct translate_ctx *ctx, + struct tgsi_texture_offset *src ) +{ + uint file; + uint swizzle[3]; + boolean parsed_swizzle; + struct parsed_bracket bracket; + + if (!parse_register_src(ctx, &file, &bracket)) + return FALSE; + + src->File = file; + src->Index = bracket.index; + + /* Parse optional swizzle. + */ + if (parse_optional_swizzle( ctx, swizzle, &parsed_swizzle, 3 )) { + if (parsed_swizzle) { + src->SwizzleX = swizzle[0]; + src->SwizzleY = swizzle[1]; + src->SwizzleZ = swizzle[2]; + } + } + + return TRUE; +} + +static boolean +match_inst(const char **pcur, + unsigned *saturate, + unsigned *precise, + const struct tgsi_opcode_info *info) +{ + const char *cur = *pcur; + + /* simple case: the whole string matches the instruction name */ + if (str_match_nocase_whole(&cur, info->mnemonic)) { + *pcur = cur; + *saturate = 0; + *precise = 0; + return TRUE; + } + + if (str_match_no_case(&cur, info->mnemonic)) { + /* the instruction has a suffix, figure it out */ + if (str_match_no_case(&cur, "_SAT")) { + *pcur = cur; + *saturate = 1; + } + + if (str_match_no_case(&cur, "_PRECISE")) { + *pcur = cur; + *precise = 1; + } + + if (!is_digit_alpha_underscore(cur)) + return TRUE; + } + + return FALSE; +} + +static boolean +parse_instruction( + struct translate_ctx *ctx, + boolean has_label ) +{ + int i; + uint saturate = 0; + uint precise = 0; + const struct tgsi_opcode_info *info; + struct tgsi_full_instruction inst; + const char *cur; + uint advance; + + inst = tgsi_default_full_instruction(); + + /* Parse instruction name. + */ + eat_opt_white( &ctx->cur ); + for (i = 0; i < TGSI_OPCODE_LAST; i++) { + cur = ctx->cur; + + info = tgsi_get_opcode_info( i ); + if (match_inst(&cur, &saturate, &precise, info)) { + if (info->num_dst + info->num_src + info->is_tex == 0) { + ctx->cur = cur; + break; + } + else if (*cur == '\0' || eat_white( &cur )) { + ctx->cur = cur; + break; + } + } + } + if (i == TGSI_OPCODE_LAST) { + if (has_label) + report_error( ctx, "Unknown opcode" ); + else + report_error( ctx, "Expected `DCL', `IMM' or a label" ); + return FALSE; + } + + inst.Instruction.Opcode = i; + inst.Instruction.Saturate = saturate; + inst.Instruction.Precise = precise; + inst.Instruction.NumDstRegs = info->num_dst; + inst.Instruction.NumSrcRegs = info->num_src; + + if (i >= TGSI_OPCODE_SAMPLE && i <= TGSI_OPCODE_GATHER4) { + /* + * These are not considered tex opcodes here (no additional + * target argument) however we're required to set the Texture + * bit so we can set the number of tex offsets. + */ + inst.Instruction.Texture = 1; + inst.Texture.Texture = TGSI_TEXTURE_UNKNOWN; + } + + if ((i >= TGSI_OPCODE_LOAD && i <= TGSI_OPCODE_ATOMIMAX) || + i == TGSI_OPCODE_RESQ) { + inst.Instruction.Memory = 1; + inst.Memory.Qualifier = 0; + } + + /* Parse instruction operands. + */ + for (i = 0; i < info->num_dst + info->num_src + info->is_tex; i++) { + if (i > 0) { + eat_opt_white( &ctx->cur ); + if (*ctx->cur != ',') { + report_error( ctx, "Expected `,'" ); + return FALSE; + } + ctx->cur++; + eat_opt_white( &ctx->cur ); + } + + if (i < info->num_dst) { + if (!parse_dst_operand( ctx, &inst.Dst[i] )) + return FALSE; + } + else if (i < info->num_dst + info->num_src) { + if (!parse_src_operand( ctx, &inst.Src[i - info->num_dst] )) + return FALSE; + } + else { + uint j; + + for (j = 0; j < TGSI_TEXTURE_COUNT; j++) { + if (str_match_nocase_whole( &ctx->cur, tgsi_texture_names[j] )) { + inst.Instruction.Texture = 1; + inst.Texture.Texture = j; + break; + } + } + if (j == TGSI_TEXTURE_COUNT) { + report_error( ctx, "Expected texture target" ); + return FALSE; + } + } + } + + cur = ctx->cur; + eat_opt_white( &cur ); + for (i = 0; inst.Instruction.Texture && *cur == ',' && i < TGSI_FULL_MAX_TEX_OFFSETS; i++) { + cur++; + eat_opt_white( &cur ); + ctx->cur = cur; + if (!parse_texoffset_operand( ctx, &inst.TexOffsets[i] )) + return FALSE; + cur = ctx->cur; + eat_opt_white( &cur ); + } + inst.Texture.NumOffsets = i; + + cur = ctx->cur; + eat_opt_white(&cur); + + for (; inst.Instruction.Memory && *cur == ','; + ctx->cur = cur, eat_opt_white(&cur)) { + int j; + + cur++; + eat_opt_white(&cur); + + j = str_match_name_from_array(&cur, tgsi_memory_names, + ARRAY_SIZE(tgsi_memory_names)); + if (j >= 0) { + inst.Memory.Qualifier |= 1U << j; + continue; + } + + j = str_match_name_from_array(&cur, tgsi_texture_names, + ARRAY_SIZE(tgsi_texture_names)); + if (j >= 0) { + inst.Memory.Texture = j; + continue; + } + + j = str_match_format(&cur); + if (j >= 0) { + inst.Memory.Format = j; + continue; + } + + ctx->cur = cur; + report_error(ctx, "Expected memory qualifier, texture target, or format\n"); + return FALSE; + } + + cur = ctx->cur; + eat_opt_white( &cur ); + if (info->is_branch && *cur == ':') { + uint target; + + cur++; + eat_opt_white( &cur ); + if (!parse_uint( &cur, &target )) { + report_error( ctx, "Expected a label" ); + return FALSE; + } + inst.Instruction.Label = 1; + inst.Label.Label = target; + ctx->cur = cur; + } + + advance = tgsi_build_full_instruction( + &inst, + ctx->tokens_cur, + ctx->header, + (uint) (ctx->tokens_end - ctx->tokens_cur) ); + if (advance == 0) + return FALSE; + ctx->tokens_cur += advance; + + return TRUE; +} + +/* parses a 4-touple of the form {x, y, z, w} + * where x, y, z, w are numbers */ +static boolean parse_immediate_data(struct translate_ctx *ctx, unsigned type, + union tgsi_immediate_data *values) +{ + unsigned i; + int ret; + + eat_opt_white( &ctx->cur ); + if (*ctx->cur != '{') { + report_error( ctx, "Expected `{'" ); + return FALSE; + } + ctx->cur++; + for (i = 0; i < 4; i++) { + eat_opt_white( &ctx->cur ); + if (i > 0) { + if (*ctx->cur != ',') { + report_error( ctx, "Expected `,'" ); + return FALSE; + } + ctx->cur++; + eat_opt_white( &ctx->cur ); + } + + switch (type) { + case TGSI_IMM_FLOAT64: + ret = parse_double(&ctx->cur, &values[i].Uint, &values[i+1].Uint); + i++; + break; + case TGSI_IMM_FLOAT32: + ret = parse_float(&ctx->cur, &values[i].Float); + break; + case TGSI_IMM_UINT32: + ret = parse_uint(&ctx->cur, &values[i].Uint); + break; + case TGSI_IMM_INT32: + ret = parse_int(&ctx->cur, &values[i].Int); + break; + default: + assert(0); + ret = FALSE; + break; + } + + if (!ret) { + report_error( ctx, "Expected immediate constant" ); + return FALSE; + } + } + eat_opt_white( &ctx->cur ); + if (*ctx->cur != '}') { + report_error( ctx, "Expected `}'" ); + return FALSE; + } + ctx->cur++; + + return TRUE; +} + +static boolean parse_declaration( struct translate_ctx *ctx ) +{ + struct tgsi_full_declaration decl; + uint file; + struct parsed_dcl_bracket brackets[2]; + int num_brackets; + uint writemask; + const char *cur, *cur2; + uint advance; + boolean is_vs_input; + + if (!eat_white( &ctx->cur )) { + report_error( ctx, "Syntax error" ); + return FALSE; + } + if (!parse_register_dcl( ctx, &file, brackets, &num_brackets)) + return FALSE; + if (!parse_opt_writemask( ctx, &writemask )) + return FALSE; + + decl = tgsi_default_full_declaration(); + decl.Declaration.File = file; + decl.Declaration.UsageMask = writemask; + + if (num_brackets == 1) { + decl.Range.First = brackets[0].first; + decl.Range.Last = brackets[0].last; + } else { + decl.Range.First = brackets[1].first; + decl.Range.Last = brackets[1].last; + + decl.Declaration.Dimension = 1; + decl.Dim.Index2D = brackets[0].first; + } + + is_vs_input = (file == TGSI_FILE_INPUT && + ctx->processor == TGSI_PROCESSOR_VERTEX); + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == ',') { + cur2 = cur; + cur2++; + eat_opt_white( &cur2 ); + if (str_match_nocase_whole( &cur2, "ARRAY" )) { + int arrayid; + if (*cur2 != '(') { + report_error( ctx, "Expected `('" ); + return FALSE; + } + cur2++; + eat_opt_white( &cur2 ); + if (!parse_int( &cur2, &arrayid )) { + report_error( ctx, "Expected `,'" ); + return FALSE; + } + eat_opt_white( &cur2 ); + if (*cur2 != ')') { + report_error( ctx, "Expected `)'" ); + return FALSE; + } + cur2++; + decl.Declaration.Array = 1; + decl.Array.ArrayID = arrayid; + ctx->cur = cur = cur2; + } + } + + if (*cur == ',' && !is_vs_input) { + uint i, j; + + cur++; + eat_opt_white( &cur ); + if (file == TGSI_FILE_IMAGE) { + for (i = 0; i < TGSI_TEXTURE_COUNT; i++) { + if (str_match_nocase_whole(&cur, tgsi_texture_names[i])) { + decl.Image.Resource = i; + break; + } + } + if (i == TGSI_TEXTURE_COUNT) { + report_error(ctx, "Expected texture target"); + return FALSE; + } + + cur2 = cur; + eat_opt_white(&cur2); + while (*cur2 == ',') { + cur2++; + eat_opt_white(&cur2); + if (str_match_nocase_whole(&cur2, "RAW")) { + decl.Image.Raw = 1; + + } else if (str_match_nocase_whole(&cur2, "WR")) { + decl.Image.Writable = 1; + + } else { + int format = str_match_format(&cur2); + if (format < 0) + break; + + decl.Image.Format = format; + } + cur = cur2; + eat_opt_white(&cur2); + } + + ctx->cur = cur; + + } else if (file == TGSI_FILE_SAMPLER_VIEW) { + for (i = 0; i < TGSI_TEXTURE_COUNT; i++) { + if (str_match_nocase_whole(&cur, tgsi_texture_names[i])) { + decl.SamplerView.Resource = i; + break; + } + } + if (i == TGSI_TEXTURE_COUNT) { + report_error(ctx, "Expected texture target"); + return FALSE; + } + eat_opt_white( &cur ); + if (*cur != ',') { + report_error( ctx, "Expected `,'" ); + return FALSE; + } + ++cur; + eat_opt_white( &cur ); + for (j = 0; j < 4; ++j) { + for (i = 0; i < TGSI_RETURN_TYPE_COUNT; ++i) { + if (str_match_nocase_whole(&cur, tgsi_return_type_names[i])) { + switch (j) { + case 0: + decl.SamplerView.ReturnTypeX = i; + break; + case 1: + decl.SamplerView.ReturnTypeY = i; + break; + case 2: + decl.SamplerView.ReturnTypeZ = i; + break; + case 3: + decl.SamplerView.ReturnTypeW = i; + break; + default: + assert(0); + } + break; + } + } + if (i == TGSI_RETURN_TYPE_COUNT) { + if (j == 0 || j > 2) { + report_error(ctx, "Expected type name"); + return FALSE; + } + break; + } else { + cur2 = cur; + eat_opt_white( &cur2 ); + if (*cur2 == ',') { + cur2++; + eat_opt_white( &cur2 ); + cur = cur2; + continue; + } else + break; + } + } + if (j < 4) { + decl.SamplerView.ReturnTypeY = + decl.SamplerView.ReturnTypeZ = + decl.SamplerView.ReturnTypeW = + decl.SamplerView.ReturnTypeX; + } + ctx->cur = cur; + } else if (file == TGSI_FILE_BUFFER) { + if (str_match_nocase_whole(&cur, "ATOMIC")) { + decl.Declaration.Atomic = 1; + ctx->cur = cur; + } + } else if (file == TGSI_FILE_MEMORY) { + if (str_match_nocase_whole(&cur, "GLOBAL")) { + /* Note this is a no-op global is the default */ + decl.Declaration.MemType = TGSI_MEMORY_TYPE_GLOBAL; + ctx->cur = cur; + } else if (str_match_nocase_whole(&cur, "SHARED")) { + decl.Declaration.MemType = TGSI_MEMORY_TYPE_SHARED; + ctx->cur = cur; + } else if (str_match_nocase_whole(&cur, "PRIVATE")) { + decl.Declaration.MemType = TGSI_MEMORY_TYPE_PRIVATE; + ctx->cur = cur; + } else if (str_match_nocase_whole(&cur, "INPUT")) { + decl.Declaration.MemType = TGSI_MEMORY_TYPE_INPUT; + ctx->cur = cur; + } + } else { + if (str_match_nocase_whole(&cur, "LOCAL")) { + decl.Declaration.Local = 1; + ctx->cur = cur; + } + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == ',') { + cur++; + eat_opt_white( &cur ); + + for (i = 0; i < TGSI_SEMANTIC_COUNT; i++) { + if (str_match_nocase_whole(&cur, tgsi_semantic_names[i])) { + uint index; + + cur2 = cur; + eat_opt_white( &cur2 ); + if (*cur2 == '[') { + cur2++; + eat_opt_white( &cur2 ); + if (!parse_uint( &cur2, &index )) { + report_error( ctx, "Expected literal integer" ); + return FALSE; + } + eat_opt_white( &cur2 ); + if (*cur2 != ']') { + report_error( ctx, "Expected `]'" ); + return FALSE; + } + cur2++; + + decl.Semantic.Index = index; + + cur = cur2; + } + + decl.Declaration.Semantic = 1; + decl.Semantic.Name = i; + + ctx->cur = cur; + break; + } + } + } + } + } + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == ',' && + file == TGSI_FILE_OUTPUT && ctx->processor == PIPE_SHADER_GEOMETRY) { + cur++; + eat_opt_white(&cur); + if (str_match_nocase_whole(&cur, "STREAM")) { + uint stream[4]; + + eat_opt_white(&cur); + if (*cur != '(') { + report_error(ctx, "Expected '('"); + return FALSE; + } + cur++; + + for (int i = 0; i < 4; ++i) { + eat_opt_white(&cur); + if (!parse_uint(&cur, &stream[i])) { + report_error(ctx, "Expected literal integer"); + return FALSE; + } + + eat_opt_white(&cur); + if (i < 3) { + if (*cur != ',') { + report_error(ctx, "Expected ','"); + return FALSE; + } + cur++; + } + } + + if (*cur != ')') { + report_error(ctx, "Expected ')'"); + return FALSE; + } + cur++; + + decl.Semantic.StreamX = stream[0]; + decl.Semantic.StreamY = stream[1]; + decl.Semantic.StreamZ = stream[2]; + decl.Semantic.StreamW = stream[3]; + + ctx->cur = cur; + } + } + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == ',' && !is_vs_input) { + uint i; + + cur++; + eat_opt_white( &cur ); + for (i = 0; i < TGSI_INTERPOLATE_COUNT; i++) { + if (str_match_nocase_whole( &cur, tgsi_interpolate_names[i] )) { + decl.Declaration.Interpolate = 1; + decl.Interp.Interpolate = i; + + ctx->cur = cur; + break; + } + } + } + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == ',' && !is_vs_input) { + uint i; + + cur++; + eat_opt_white( &cur ); + for (i = 0; i < TGSI_INTERPOLATE_LOC_COUNT; i++) { + if (str_match_nocase_whole( &cur, tgsi_interpolate_locations[i] )) { + decl.Interp.Location = i; + + ctx->cur = cur; + break; + } + } + } + + cur = ctx->cur; + eat_opt_white( &cur ); + if (*cur == ',' && !is_vs_input) { + cur++; + eat_opt_white( &cur ); + if (str_match_nocase_whole( &cur, tgsi_invariant_name )) { + decl.Declaration.Invariant = 1; + ctx->cur = cur; + } else { + report_error( ctx, "Expected semantic, interpolate attribute, or invariant \"%.10s...\" ", cur ); + return FALSE; + } + } + + advance = tgsi_build_full_declaration( + &decl, + ctx->tokens_cur, + ctx->header, + (uint) (ctx->tokens_end - ctx->tokens_cur) ); + + if (advance == 0) + return FALSE; + ctx->tokens_cur += advance; + + return TRUE; +} + +static boolean parse_immediate( struct translate_ctx *ctx ) +{ + struct tgsi_full_immediate imm; + uint advance; + uint type; + + if (*ctx->cur == '[') { + uint uindex; + + ++ctx->cur; + + eat_opt_white( &ctx->cur ); + if (!parse_uint( &ctx->cur, &uindex )) { + report_error( ctx, "Expected literal unsigned integer" ); + return FALSE; + } + + if (uindex != ctx->num_immediates) { + report_error( ctx, "Immediates must be sorted" ); + return FALSE; + } + + eat_opt_white( &ctx->cur ); + if (*ctx->cur != ']') { + report_error( ctx, "Expected `]'" ); + return FALSE; + } + + ctx->cur++; + } + + if (!eat_white( &ctx->cur )) { + report_error( ctx, "Syntax error" ); + return FALSE; + } + for (type = 0; type < Elements(tgsi_immediate_type_names); ++type) { + if (str_match_nocase_whole(&ctx->cur, tgsi_immediate_type_names[type])) + break; + } + if (type == Elements(tgsi_immediate_type_names)) { + report_error( ctx, "Expected immediate type" ); + return FALSE; + } + + imm = tgsi_default_full_immediate(); + imm.Immediate.NrTokens += 4; + imm.Immediate.DataType = type; + parse_immediate_data(ctx, type, imm.u); + + advance = tgsi_build_full_immediate( + &imm, + ctx->tokens_cur, + ctx->header, + (uint) (ctx->tokens_end - ctx->tokens_cur) ); + if (advance == 0) + return FALSE; + ctx->tokens_cur += advance; + + ctx->num_immediates++; + + return TRUE; +} + +static boolean +parse_primitive( const char **pcur, uint *primitive ) +{ + uint i; + + for (i = 0; i < PIPE_PRIM_MAX; i++) { + const char *cur = *pcur; + + if (str_match_nocase_whole( &cur, tgsi_primitive_names[i])) { + *primitive = i; + *pcur = cur; + return TRUE; + } + } + return FALSE; +} + +static boolean +parse_fs_coord_origin( const char **pcur, uint *fs_coord_origin ) +{ + uint i; + + for (i = 0; i < Elements(tgsi_fs_coord_origin_names); i++) { + const char *cur = *pcur; + + if (str_match_nocase_whole( &cur, tgsi_fs_coord_origin_names[i])) { + *fs_coord_origin = i; + *pcur = cur; + return TRUE; + } + } + return FALSE; +} + +static boolean +parse_fs_coord_pixel_center( const char **pcur, uint *fs_coord_pixel_center ) +{ + uint i; + + for (i = 0; i < Elements(tgsi_fs_coord_pixel_center_names); i++) { + const char *cur = *pcur; + + if (str_match_nocase_whole( &cur, tgsi_fs_coord_pixel_center_names[i])) { + *fs_coord_pixel_center = i; + *pcur = cur; + return TRUE; + } + } + return FALSE; +} + +static boolean +parse_property_next_shader( const char **pcur, uint *next_shader ) +{ + uint i; + + for (i = 0; i < ARRAY_SIZE(tgsi_processor_type_names); i++) { + const char *cur = *pcur; + + if (str_match_nocase_whole( &cur, tgsi_processor_type_names[i])) { + *next_shader = i; + *pcur = cur; + return TRUE; + } + } + return FALSE; +} + +static boolean parse_property( struct translate_ctx *ctx ) +{ + struct tgsi_full_property prop; + uint property_name; + uint values[8]; + uint advance; + char id[64]; + + if (!eat_white( &ctx->cur )) { + report_error( ctx, "Syntax error" ); + return FALSE; + } + if (!parse_identifier( &ctx->cur, id, sizeof(id) )) { + report_error( ctx, "Syntax error" ); + return FALSE; + } + for (property_name = 0; property_name < TGSI_PROPERTY_COUNT; + ++property_name) { + if (streq_nocase_uprcase(tgsi_property_names[property_name], id)) { + break; + } + } + if (property_name >= TGSI_PROPERTY_COUNT) { + eat_until_eol( &ctx->cur ); + report_error(ctx, "\nError: Unknown property : '%s'\n", id); + return TRUE; + } + + eat_opt_white( &ctx->cur ); + switch(property_name) { + case TGSI_PROPERTY_GS_INPUT_PRIM: + case TGSI_PROPERTY_GS_OUTPUT_PRIM: + if (!parse_primitive(&ctx->cur, &values[0] )) { + report_error( ctx, "Unknown primitive name as property!" ); + return FALSE; + } + if (property_name == TGSI_PROPERTY_GS_INPUT_PRIM && + ctx->processor == TGSI_PROCESSOR_GEOMETRY) { + ctx->implied_array_size = u_vertices_per_prim(values[0]); + } + break; + case TGSI_PROPERTY_FS_COORD_ORIGIN: + if (!parse_fs_coord_origin(&ctx->cur, &values[0] )) { + report_error( ctx, "Unknown coord origin as property: must be UPPER_LEFT or LOWER_LEFT!" ); + return FALSE; + } + break; + case TGSI_PROPERTY_FS_COORD_PIXEL_CENTER: + if (!parse_fs_coord_pixel_center(&ctx->cur, &values[0] )) { + report_error( ctx, "Unknown coord pixel center as property: must be HALF_INTEGER or INTEGER!" ); + return FALSE; + } + break; + case TGSI_PROPERTY_NEXT_SHADER: + if (!parse_property_next_shader(&ctx->cur, &values[0] )) { + report_error( ctx, "Unknown next shader property value." ); + return FALSE; + } + break; + case TGSI_PROPERTY_FS_COLOR0_WRITES_ALL_CBUFS: + default: + if (!parse_uint(&ctx->cur, &values[0] )) { + report_error( ctx, "Expected unsigned integer as property!" ); + return FALSE; + } + } + + prop = tgsi_default_full_property(); + prop.Property.PropertyName = property_name; + prop.Property.NrTokens += 1; + prop.u[0].Data = values[0]; + + advance = tgsi_build_full_property( + &prop, + ctx->tokens_cur, + ctx->header, + (uint) (ctx->tokens_end - ctx->tokens_cur) ); + if (advance == 0) + return FALSE; + ctx->tokens_cur += advance; + + return TRUE; +} + + +static boolean translate( struct translate_ctx *ctx ) +{ + eat_opt_white( &ctx->cur ); + if (!parse_header( ctx )) + return FALSE; + + if (ctx->processor == TGSI_PROCESSOR_TESS_CTRL || + ctx->processor == TGSI_PROCESSOR_TESS_EVAL) + ctx->implied_array_size = 32; + + while (*ctx->cur != '\0') { + uint label_val = 0; + if (!eat_white( &ctx->cur )) { + report_error( ctx, "Syntax error" ); + return FALSE; + } + + if (*ctx->cur == '\0') + break; + if (parse_label( ctx, &label_val )) { + if (!parse_instruction( ctx, TRUE )) + return FALSE; + } + else if (str_match_nocase_whole( &ctx->cur, "DCL" )) { + if (!parse_declaration( ctx )) + return FALSE; + } + else if (str_match_nocase_whole( &ctx->cur, "IMM" )) { + if (!parse_immediate( ctx )) + return FALSE; + } + else if (str_match_nocase_whole( &ctx->cur, "PROPERTY" )) { + if (!parse_property( ctx )) + return FALSE; + } + else if (!parse_instruction( ctx, FALSE )) { + return FALSE; + } + } + + return TRUE; +} + +boolean +tgsi_text_translate( + const char *text, + struct tgsi_token *tokens, + uint num_tokens ) +{ + struct translate_ctx ctx = {0}; + + ctx.text = text; + ctx.cur = text; + ctx.tokens = tokens; + ctx.tokens_cur = tokens; + ctx.tokens_end = tokens + num_tokens; + + if (!translate( &ctx )) + return FALSE; + + return tgsi_sanity_check( tokens ); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_text.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_text.h new file mode 100644 index 0000000..6a306e6 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_text.h @@ -0,0 +1,49 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_TEXT_H +#define TGSI_TEXT_H + +#if defined __cplusplus +extern "C" { +#endif + +#include "pipe/p_compiler.h" + +struct tgsi_token; + +boolean +tgsi_text_translate( + const char *text, + struct tgsi_token *tokens, + uint num_tokens ); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_TEXT_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_transform.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_transform.c new file mode 100644 index 0000000..b16d296 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_transform.c @@ -0,0 +1,250 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * TGSI program transformation utility. + * + * Authors: Brian Paul + */ + +#include "util/u_debug.h" + +#include "tgsi_transform.h" + + + +static void +emit_instruction(struct tgsi_transform_context *ctx, + const struct tgsi_full_instruction *inst) +{ + uint ti = ctx->ti; + + ti += tgsi_build_full_instruction(inst, + ctx->tokens_out + ti, + ctx->header, + ctx->max_tokens_out - ti); + ctx->ti = ti; +} + + +static void +emit_declaration(struct tgsi_transform_context *ctx, + const struct tgsi_full_declaration *decl) +{ + uint ti = ctx->ti; + + ti += tgsi_build_full_declaration(decl, + ctx->tokens_out + ti, + ctx->header, + ctx->max_tokens_out - ti); + ctx->ti = ti; +} + + +static void +emit_immediate(struct tgsi_transform_context *ctx, + const struct tgsi_full_immediate *imm) +{ + uint ti = ctx->ti; + + ti += tgsi_build_full_immediate(imm, + ctx->tokens_out + ti, + ctx->header, + ctx->max_tokens_out - ti); + ctx->ti = ti; +} + + +static void +emit_property(struct tgsi_transform_context *ctx, + const struct tgsi_full_property *prop) +{ + uint ti = ctx->ti; + + ti += tgsi_build_full_property(prop, + ctx->tokens_out + ti, + ctx->header, + ctx->max_tokens_out - ti); + ctx->ti = ti; +} + + +/** + * Apply user-defined transformations to the input shader to produce + * the output shader. + * For example, a register search-and-replace operation could be applied + * by defining a transform_instruction() callback that examined and changed + * the instruction src/dest regs. + * + * \return number of tokens emitted + */ +int +tgsi_transform_shader(const struct tgsi_token *tokens_in, + struct tgsi_token *tokens_out, + uint max_tokens_out, + struct tgsi_transform_context *ctx) +{ + uint procType; + + /* input shader */ + struct tgsi_parse_context parse; + + /* output shader */ + struct tgsi_processor *processor; + + + /** + ** callback context init + **/ + ctx->emit_instruction = emit_instruction; + ctx->emit_declaration = emit_declaration; + ctx->emit_immediate = emit_immediate; + ctx->emit_property = emit_property; + ctx->tokens_out = tokens_out; + ctx->max_tokens_out = max_tokens_out; + + + /** + ** Setup to begin parsing input shader + **/ + if (tgsi_parse_init( &parse, tokens_in ) != TGSI_PARSE_OK) { + debug_printf("tgsi_parse_init() failed in tgsi_transform_shader()!\n"); + return -1; + } + procType = parse.FullHeader.Processor.Processor; + assert(procType == TGSI_PROCESSOR_FRAGMENT || + procType == TGSI_PROCESSOR_VERTEX || + procType == TGSI_PROCESSOR_GEOMETRY); + + + /** + ** Setup output shader + **/ + ctx->header = (struct tgsi_header *)tokens_out; + *ctx->header = tgsi_build_header(); + + processor = (struct tgsi_processor *) (tokens_out + 1); + *processor = tgsi_build_processor( procType, ctx->header ); + + ctx->ti = 2; + + + /** + ** Loop over incoming program tokens/instructions + */ + while( !tgsi_parse_end_of_tokens( &parse ) ) { + + tgsi_parse_token( &parse ); + + switch( parse.FullToken.Token.Type ) { + case TGSI_TOKEN_TYPE_INSTRUCTION: + { + struct tgsi_full_instruction *fullinst + = &parse.FullToken.FullInstruction; + + if (ctx->transform_instruction) + ctx->transform_instruction(ctx, fullinst); + else + ctx->emit_instruction(ctx, fullinst); + } + break; + + case TGSI_TOKEN_TYPE_DECLARATION: + { + struct tgsi_full_declaration *fulldecl + = &parse.FullToken.FullDeclaration; + + if (ctx->transform_declaration) + ctx->transform_declaration(ctx, fulldecl); + else + ctx->emit_declaration(ctx, fulldecl); + } + break; + + case TGSI_TOKEN_TYPE_IMMEDIATE: + { + struct tgsi_full_immediate *fullimm + = &parse.FullToken.FullImmediate; + + if (ctx->transform_immediate) + ctx->transform_immediate(ctx, fullimm); + else + ctx->emit_immediate(ctx, fullimm); + } + break; + case TGSI_TOKEN_TYPE_PROPERTY: + { + struct tgsi_full_property *fullprop + = &parse.FullToken.FullProperty; + + if (ctx->transform_property) + ctx->transform_property(ctx, fullprop); + else + ctx->emit_property(ctx, fullprop); + } + break; + + default: + assert( 0 ); + } + } + + if (ctx->epilog) { + ctx->epilog(ctx); + } + + tgsi_parse_free (&parse); + + return ctx->ti; +} + + +#include "tgsi_text.h" + +extern int tgsi_transform_foo( struct tgsi_token *tokens_out, + uint max_tokens_out ); + +/* This function exists only so that tgsi_text_translate() doesn't get + * magic-ed out of the libtgsi.a archive by the build system. Don't + * remove unless you know this has been fixed - check on mingw/scons + * builds as well. + */ +int +tgsi_transform_foo( struct tgsi_token *tokens_out, + uint max_tokens_out ) +{ + const char *text = + "FRAG\n" + "DCL IN[0], COLOR, CONSTANT\n" + "DCL OUT[0], COLOR\n" + " 0: MOV OUT[0], IN[0]\n" + " 1: END"; + + return tgsi_text_translate( text, + tokens_out, + max_tokens_out ); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_transform.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_transform.h new file mode 100644 index 0000000..8b96664 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_transform.h @@ -0,0 +1,96 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_TRANSFORM_H +#define TGSI_TRANSFORM_H + + +#include "pipe/p_shader_tokens.h" +#include "tgsi/tgsi_parse.h" +#include "tgsi/tgsi_build.h" + + + +/** + * Subclass this to add caller-specific data + */ +struct tgsi_transform_context +{ +/**** PUBLIC ***/ + + /** + * User-defined callbacks invoked per instruction. + */ + void (*transform_instruction)(struct tgsi_transform_context *ctx, + struct tgsi_full_instruction *inst); + + void (*transform_declaration)(struct tgsi_transform_context *ctx, + struct tgsi_full_declaration *decl); + + void (*transform_immediate)(struct tgsi_transform_context *ctx, + struct tgsi_full_immediate *imm); + void (*transform_property)(struct tgsi_transform_context *ctx, + struct tgsi_full_property *prop); + + /** + * Called at end of input program to allow caller to append extra + * instructions. Return number of tokens emitted. + */ + void (*epilog)(struct tgsi_transform_context *ctx); + + +/*** PRIVATE ***/ + + /** + * These are setup by tgsi_transform_shader() and cannot be overridden. + * Meant to be called from in the above user callback functions. + */ + void (*emit_instruction)(struct tgsi_transform_context *ctx, + const struct tgsi_full_instruction *inst); + void (*emit_declaration)(struct tgsi_transform_context *ctx, + const struct tgsi_full_declaration *decl); + void (*emit_immediate)(struct tgsi_transform_context *ctx, + const struct tgsi_full_immediate *imm); + void (*emit_property)(struct tgsi_transform_context *ctx, + const struct tgsi_full_property *prop); + + struct tgsi_header *header; + uint max_tokens_out; + struct tgsi_token *tokens_out; + uint ti; +}; + + + +extern int +tgsi_transform_shader(const struct tgsi_token *tokens_in, + struct tgsi_token *tokens_out, + uint max_tokens_out, + struct tgsi_transform_context *ctx); + + +#endif /* TGSI_TRANSFORM_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_ureg.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_ureg.c new file mode 100644 index 0000000..ded78c2 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_ureg.c @@ -0,0 +1,1736 @@ +/************************************************************************** + * + * Copyright 2009-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE, INC AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#include "pipe/p_context.h" +#include "pipe/p_state.h" +#include "tgsi/tgsi_ureg.h" +#include "tgsi/tgsi_build.h" +#include "tgsi/tgsi_info.h" +#include "tgsi/tgsi_dump.h" +#include "tgsi/tgsi_sanity.h" +#include "util/u_debug.h" +#include "util/u_memory.h" +#include "util/u_math.h" +#include "util/u_bitmask.h" + +union tgsi_any_token { + struct tgsi_header header; + struct tgsi_processor processor; + struct tgsi_token token; + struct tgsi_property prop; + struct tgsi_property_data prop_data; + struct tgsi_declaration decl; + struct tgsi_declaration_range decl_range; + struct tgsi_declaration_dimension decl_dim; + struct tgsi_declaration_interp decl_interp; + struct tgsi_declaration_semantic decl_semantic; + struct tgsi_declaration_sampler_view decl_sampler_view; + struct tgsi_declaration_array array; + struct tgsi_immediate imm; + union tgsi_immediate_data imm_data; + struct tgsi_instruction insn; + struct tgsi_instruction_label insn_label; + struct tgsi_instruction_texture insn_texture; + struct tgsi_texture_offset insn_texture_offset; + struct tgsi_src_register src; + struct tgsi_ind_register ind; + struct tgsi_dimension dim; + struct tgsi_dst_register dst; + unsigned value; +}; + + +struct ureg_tokens { + union tgsi_any_token *tokens; + unsigned size; + unsigned order; + unsigned count; +}; + +#define UREG_MAX_INPUT PIPE_MAX_ATTRIBS +#define UREG_MAX_SYSTEM_VALUE PIPE_MAX_ATTRIBS +#define UREG_MAX_OUTPUT PIPE_MAX_SHADER_OUTPUTS +#define UREG_MAX_CONSTANT_RANGE 32 +#define UREG_MAX_HW_ATOMIC_RANGE 32 +#define UREG_MAX_IMMEDIATE 4096 +#define UREG_MAX_ADDR 3 +#define UREG_MAX_PRED 1 +#define UREG_MAX_ARRAY_TEMPS 256 + +struct const_decl { + struct { + unsigned first; + unsigned last; + } constant_range[UREG_MAX_CONSTANT_RANGE]; + unsigned nr_constant_ranges; +}; + +struct hw_atomic_decl { + struct { + unsigned first; + unsigned last; + unsigned array_id; + } hw_atomic_range[UREG_MAX_HW_ATOMIC_RANGE]; + unsigned nr_hw_atomic_ranges; +}; + +#define DOMAIN_DECL 0 +#define DOMAIN_INSN 1 + +struct ureg_program +{ + unsigned processor; + struct pipe_context *pipe; + + struct { + unsigned semantic_name; + unsigned semantic_index; + unsigned interp; + unsigned char cylindrical_wrap; + unsigned interp_location; + } fs_input[UREG_MAX_INPUT]; + unsigned nr_fs_inputs; + + unsigned vs_inputs[UREG_MAX_INPUT/32]; + + struct { + unsigned index; + unsigned semantic_name; + unsigned semantic_index; + } gs_input[UREG_MAX_INPUT]; + unsigned nr_gs_inputs; + + struct { + unsigned index; + unsigned semantic_name; + unsigned semantic_index; + } system_value[UREG_MAX_SYSTEM_VALUE]; + unsigned nr_system_values; + + struct { + unsigned semantic_name; + unsigned semantic_index; + unsigned usage_mask; /* = TGSI_WRITEMASK_* */ + } output[UREG_MAX_OUTPUT]; + unsigned nr_outputs; + + struct { + union { + float f[4]; + unsigned u[4]; + int i[4]; + } value; + unsigned nr; + unsigned type; + } immediate[UREG_MAX_IMMEDIATE]; + unsigned nr_immediates; + + struct ureg_src sampler[PIPE_MAX_SAMPLERS]; + unsigned nr_samplers; + + struct { + unsigned index; + unsigned target; + unsigned return_type_x; + unsigned return_type_y; + unsigned return_type_z; + unsigned return_type_w; + } sampler_view[PIPE_MAX_SHADER_SAMPLER_VIEWS]; + unsigned nr_sampler_views; + + struct util_bitmask *free_temps; + struct util_bitmask *local_temps; + struct util_bitmask *decl_temps; + unsigned nr_temps; + + unsigned array_temps[UREG_MAX_ARRAY_TEMPS]; + unsigned nr_array_temps; + + struct const_decl const_decls; + struct const_decl const_decls2D[PIPE_MAX_CONSTANT_BUFFERS]; + + struct hw_atomic_decl hw_atomic_decls[PIPE_MAX_HW_ATOMIC_BUFFERS]; + + unsigned properties[TGSI_PROPERTY_COUNT]; + + unsigned nr_addrs; + unsigned nr_preds; + unsigned nr_instructions; + + struct ureg_tokens domain[2]; +}; + +static union tgsi_any_token error_tokens[32]; + +static void tokens_error( struct ureg_tokens *tokens ) +{ + if (tokens->tokens && tokens->tokens != error_tokens) + FREE(tokens->tokens); + + tokens->tokens = error_tokens; + tokens->size = ARRAY_SIZE(error_tokens); + tokens->count = 0; +} + + +static void tokens_expand( struct ureg_tokens *tokens, + unsigned count ) +{ + unsigned old_size = tokens->size * sizeof(unsigned); + + if (tokens->tokens == error_tokens) { + return; + } + + while (tokens->count + count > tokens->size) { + tokens->size = (1 << ++tokens->order); + } + + tokens->tokens = REALLOC(tokens->tokens, + old_size, + tokens->size * sizeof(unsigned)); + if (tokens->tokens == NULL) { + tokens_error(tokens); + } +} + +static void set_bad( struct ureg_program *ureg ) +{ + tokens_error(&ureg->domain[0]); +} + + + +static union tgsi_any_token *get_tokens( struct ureg_program *ureg, + unsigned domain, + unsigned count ) +{ + struct ureg_tokens *tokens = &ureg->domain[domain]; + union tgsi_any_token *result; + + if (tokens->count + count > tokens->size) + tokens_expand(tokens, count); + + result = &tokens->tokens[tokens->count]; + tokens->count += count; + return result; +} + + +static union tgsi_any_token *retrieve_token( struct ureg_program *ureg, + unsigned domain, + unsigned nr ) +{ + if (ureg->domain[domain].tokens == error_tokens) + return &error_tokens[0]; + + return &ureg->domain[domain].tokens[nr]; +} + + + +static inline struct ureg_dst +ureg_dst_register( unsigned file, + unsigned index ) +{ + struct ureg_dst dst; + + dst.File = file; + dst.WriteMask = TGSI_WRITEMASK_XYZW; + dst.Indirect = 0; + dst.IndirectFile = TGSI_FILE_NULL; + dst.IndirectIndex = 0; + dst.IndirectSwizzle = 0; + dst.Saturate = 0; + dst.PredNegate = 0; + dst.PredSwizzleX = TGSI_SWIZZLE_X; + dst.PredSwizzleY = TGSI_SWIZZLE_Y; + dst.PredSwizzleZ = TGSI_SWIZZLE_Z; + dst.PredSwizzleW = TGSI_SWIZZLE_W; + dst.Index = index; + dst.ArrayID = 0; + + return dst; +} + + +void +ureg_property(struct ureg_program *ureg, unsigned name, unsigned value) +{ + assert(name < ARRAY_SIZE(ureg->properties)); + ureg->properties[name] = value; +} + +struct ureg_src +ureg_DECL_fs_input_cyl_centroid(struct ureg_program *ureg, + unsigned semantic_name, + unsigned semantic_index, + unsigned interp_mode, + unsigned cylindrical_wrap, + unsigned interp_location) +{ + unsigned i; + + for (i = 0; i < ureg->nr_fs_inputs; i++) { + if (ureg->fs_input[i].semantic_name == semantic_name && + ureg->fs_input[i].semantic_index == semantic_index) { + goto out; + } + } + + if (ureg->nr_fs_inputs < UREG_MAX_INPUT) { + ureg->fs_input[i].semantic_name = semantic_name; + ureg->fs_input[i].semantic_index = semantic_index; + ureg->fs_input[i].interp = interp_mode; + ureg->fs_input[i].cylindrical_wrap = cylindrical_wrap; + ureg->fs_input[i].interp_location = interp_location; + ureg->nr_fs_inputs++; + } else { + set_bad(ureg); + } + +out: + return ureg_src_register(TGSI_FILE_INPUT, i); +} + + +struct ureg_src +ureg_DECL_vs_input( struct ureg_program *ureg, + unsigned index ) +{ + assert(ureg->processor == TGSI_PROCESSOR_VERTEX); + + ureg->vs_inputs[index/32] |= 1 << (index % 32); + return ureg_src_register( TGSI_FILE_INPUT, index ); +} + + +struct ureg_src +ureg_DECL_gs_input(struct ureg_program *ureg, + unsigned index, + unsigned semantic_name, + unsigned semantic_index) +{ + if (ureg->nr_gs_inputs < UREG_MAX_INPUT) { + ureg->gs_input[ureg->nr_gs_inputs].index = index; + ureg->gs_input[ureg->nr_gs_inputs].semantic_name = semantic_name; + ureg->gs_input[ureg->nr_gs_inputs].semantic_index = semantic_index; + ureg->nr_gs_inputs++; + } else { + set_bad(ureg); + } + + /* XXX: Add suport for true 2D input registers. */ + return ureg_src_register(TGSI_FILE_INPUT, index); +} + + +struct ureg_src +ureg_DECL_system_value(struct ureg_program *ureg, + unsigned index, + unsigned semantic_name, + unsigned semantic_index) +{ + if (ureg->nr_system_values < UREG_MAX_SYSTEM_VALUE) { + ureg->system_value[ureg->nr_system_values].index = index; + ureg->system_value[ureg->nr_system_values].semantic_name = semantic_name; + ureg->system_value[ureg->nr_system_values].semantic_index = semantic_index; + ureg->nr_system_values++; + } else { + set_bad(ureg); + } + + return ureg_src_register(TGSI_FILE_SYSTEM_VALUE, index); +} + + +struct ureg_dst +ureg_DECL_output_masked( struct ureg_program *ureg, + unsigned name, + unsigned index, + unsigned usage_mask ) +{ + unsigned i; + + assert(usage_mask != 0); + + for (i = 0; i < ureg->nr_outputs; i++) { + if (ureg->output[i].semantic_name == name && + ureg->output[i].semantic_index == index) { + ureg->output[i].usage_mask |= usage_mask; + goto out; + } + } + + if (ureg->nr_outputs < UREG_MAX_OUTPUT) { + ureg->output[i].semantic_name = name; + ureg->output[i].semantic_index = index; + ureg->output[i].usage_mask = usage_mask; + ureg->nr_outputs++; + } + else { + set_bad( ureg ); + } + +out: + return ureg_dst_register( TGSI_FILE_OUTPUT, i ); +} + + +struct ureg_dst +ureg_DECL_output( struct ureg_program *ureg, + unsigned name, + unsigned index ) +{ + return ureg_DECL_output_masked(ureg, name, index, TGSI_WRITEMASK_XYZW); +} + + +/* Returns a new constant register. Keep track of which have been + * referred to so that we can emit decls later. + * + * Constant operands declared with this function must be addressed + * with a two-dimensional index. + * + * There is nothing in this code to bind this constant to any tracked + * value or manage any constant_buffer contents -- that's the + * resposibility of the calling code. + */ +void +ureg_DECL_constant2D(struct ureg_program *ureg, + unsigned first, + unsigned last, + unsigned index2D) +{ + struct const_decl *decl = &ureg->const_decls2D[index2D]; + + assert(index2D < PIPE_MAX_CONSTANT_BUFFERS); + + if (decl->nr_constant_ranges < UREG_MAX_CONSTANT_RANGE) { + uint i = decl->nr_constant_ranges++; + + decl->constant_range[i].first = first; + decl->constant_range[i].last = last; + } +} + + +/* A one-dimensional, depricated version of ureg_DECL_constant2D(). + * + * Constant operands declared with this function must be addressed + * with a one-dimensional index. + */ +struct ureg_src +ureg_DECL_constant(struct ureg_program *ureg, + unsigned index) +{ + struct const_decl *decl = &ureg->const_decls; + unsigned minconst = index, maxconst = index; + unsigned i; + + /* Inside existing range? + */ + for (i = 0; i < decl->nr_constant_ranges; i++) { + if (decl->constant_range[i].first <= index && + decl->constant_range[i].last >= index) { + goto out; + } + } + + /* Extend existing range? + */ + for (i = 0; i < decl->nr_constant_ranges; i++) { + if (decl->constant_range[i].last == index - 1) { + decl->constant_range[i].last = index; + goto out; + } + + if (decl->constant_range[i].first == index + 1) { + decl->constant_range[i].first = index; + goto out; + } + + minconst = MIN2(minconst, decl->constant_range[i].first); + maxconst = MAX2(maxconst, decl->constant_range[i].last); + } + + /* Create new range? + */ + if (decl->nr_constant_ranges < UREG_MAX_CONSTANT_RANGE) { + i = decl->nr_constant_ranges++; + decl->constant_range[i].first = index; + decl->constant_range[i].last = index; + goto out; + } + + /* Collapse all ranges down to one: + */ + i = 0; + decl->constant_range[0].first = minconst; + decl->constant_range[0].last = maxconst; + decl->nr_constant_ranges = 1; + +out: + assert(i < decl->nr_constant_ranges); + assert(decl->constant_range[i].first <= index); + assert(decl->constant_range[i].last >= index); + return ureg_src_register(TGSI_FILE_CONSTANT, index); +} + + +/* Returns a new hw atomic register. Keep track of which have been + * referred to so that we can emit decls later. + */ +void +ureg_DECL_hw_atomic(struct ureg_program *ureg, + unsigned first, + unsigned last, + unsigned buffer_id, + unsigned array_id) +{ + struct hw_atomic_decl *decl = &ureg->hw_atomic_decls[buffer_id]; + + if (decl->nr_hw_atomic_ranges < UREG_MAX_HW_ATOMIC_RANGE) { + uint i = decl->nr_hw_atomic_ranges++; + + decl->hw_atomic_range[i].first = first; + decl->hw_atomic_range[i].last = last; + decl->hw_atomic_range[i].array_id = array_id; + } else { + set_bad(ureg); + } +} + +static struct ureg_dst alloc_temporary( struct ureg_program *ureg, + boolean local ) +{ + unsigned i; + + /* Look for a released temporary. + */ + for (i = util_bitmask_get_first_index(ureg->free_temps); + i != UTIL_BITMASK_INVALID_INDEX; + i = util_bitmask_get_next_index(ureg->free_temps, i + 1)) { + if (util_bitmask_get(ureg->local_temps, i) == local) + break; + } + + /* Or allocate a new one. + */ + if (i == UTIL_BITMASK_INVALID_INDEX) { + i = ureg->nr_temps++; + + if (local) + util_bitmask_set(ureg->local_temps, i); + + /* Start a new declaration when the local flag changes */ + if (!i || util_bitmask_get(ureg->local_temps, i - 1) != local) + util_bitmask_set(ureg->decl_temps, i); + } + + util_bitmask_clear(ureg->free_temps, i); + + return ureg_dst_register( TGSI_FILE_TEMPORARY, i ); +} + +struct ureg_dst ureg_DECL_temporary( struct ureg_program *ureg ) +{ + return alloc_temporary(ureg, FALSE); +} + +struct ureg_dst ureg_DECL_local_temporary( struct ureg_program *ureg ) +{ + return alloc_temporary(ureg, TRUE); +} + +struct ureg_dst ureg_DECL_array_temporary( struct ureg_program *ureg, + unsigned size, + boolean local ) +{ + unsigned i = ureg->nr_temps; + struct ureg_dst dst = ureg_dst_register( TGSI_FILE_TEMPORARY, i ); + + if (local) + util_bitmask_set(ureg->local_temps, i); + + /* Always start a new declaration at the start */ + util_bitmask_set(ureg->decl_temps, i); + + ureg->nr_temps += size; + + /* and also at the end of the array */ + util_bitmask_set(ureg->decl_temps, ureg->nr_temps); + + if (ureg->nr_array_temps < UREG_MAX_ARRAY_TEMPS) { + ureg->array_temps[ureg->nr_array_temps++] = i; + dst.ArrayID = ureg->nr_array_temps; + } + + return dst; +} + +void ureg_release_temporary( struct ureg_program *ureg, + struct ureg_dst tmp ) +{ + if(tmp.File == TGSI_FILE_TEMPORARY) + util_bitmask_set(ureg->free_temps, tmp.Index); +} + + +/* Allocate a new address register. + */ +struct ureg_dst ureg_DECL_address( struct ureg_program *ureg ) +{ + if (ureg->nr_addrs < UREG_MAX_ADDR) + return ureg_dst_register( TGSI_FILE_ADDRESS, ureg->nr_addrs++ ); + + assert( 0 ); + return ureg_dst_register( TGSI_FILE_ADDRESS, 0 ); +} + +/* Allocate a new predicate register. + */ +struct ureg_dst +ureg_DECL_predicate(struct ureg_program *ureg) +{ + if (ureg->nr_preds < UREG_MAX_PRED) { + return ureg_dst_register(TGSI_FILE_PREDICATE, ureg->nr_preds++); + } + + assert(0); + return ureg_dst_register(TGSI_FILE_PREDICATE, 0); +} + +/* Allocate a new sampler. + */ +struct ureg_src ureg_DECL_sampler( struct ureg_program *ureg, + int nr ) +{ + unsigned i; + + for (i = 0; i < ureg->nr_samplers; i++) + if (ureg->sampler[i].Index == nr) + return ureg->sampler[i]; + + if (i < PIPE_MAX_SAMPLERS) { + ureg->sampler[i] = ureg_src_register( TGSI_FILE_SAMPLER, nr ); + ureg->nr_samplers++; + return ureg->sampler[i]; + } + + assert( 0 ); + return ureg->sampler[0]; +} + +/* + * Allocate a new shader sampler view. + */ +struct ureg_src +ureg_DECL_sampler_view(struct ureg_program *ureg, + unsigned index, + unsigned target, + unsigned return_type_x, + unsigned return_type_y, + unsigned return_type_z, + unsigned return_type_w) +{ + struct ureg_src reg = ureg_src_register(TGSI_FILE_SAMPLER_VIEW, index); + uint i; + + for (i = 0; i < ureg->nr_sampler_views; i++) { + if (ureg->sampler_view[i].index == index) { + return reg; + } + } + + if (i < PIPE_MAX_SHADER_SAMPLER_VIEWS) { + ureg->sampler_view[i].index = index; + ureg->sampler_view[i].target = target; + ureg->sampler_view[i].return_type_x = return_type_x; + ureg->sampler_view[i].return_type_y = return_type_y; + ureg->sampler_view[i].return_type_z = return_type_z; + ureg->sampler_view[i].return_type_w = return_type_w; + ureg->nr_sampler_views++; + return reg; + } + + assert(0); + return reg; +} + +static int +match_or_expand_immediate( const unsigned *v, + unsigned nr, + unsigned *v2, + unsigned *pnr2, + unsigned *swizzle ) +{ + unsigned nr2 = *pnr2; + unsigned i, j; + + *swizzle = 0; + + for (i = 0; i < nr; i++) { + boolean found = FALSE; + + for (j = 0; j < nr2 && !found; j++) { + if (v[i] == v2[j]) { + *swizzle |= j << (i * 2); + found = TRUE; + } + } + + if (!found) { + if (nr2 >= 4) { + return FALSE; + } + + v2[nr2] = v[i]; + *swizzle |= nr2 << (i * 2); + nr2++; + } + } + + /* Actually expand immediate only when fully succeeded. + */ + *pnr2 = nr2; + return TRUE; +} + + +static struct ureg_src +decl_immediate( struct ureg_program *ureg, + const unsigned *v, + unsigned nr, + unsigned type ) +{ + unsigned i, j; + unsigned swizzle = 0; + + /* Could do a first pass where we examine all existing immediates + * without expanding. + */ + + for (i = 0; i < ureg->nr_immediates; i++) { + if (ureg->immediate[i].type != type) { + continue; + } + if (match_or_expand_immediate(v, + nr, + ureg->immediate[i].value.u, + &ureg->immediate[i].nr, + &swizzle)) { + goto out; + } + } + + if (ureg->nr_immediates < UREG_MAX_IMMEDIATE) { + i = ureg->nr_immediates++; + ureg->immediate[i].type = type; + if (match_or_expand_immediate(v, + nr, + ureg->immediate[i].value.u, + &ureg->immediate[i].nr, + &swizzle)) { + goto out; + } + } + + set_bad(ureg); + +out: + /* Make sure that all referenced elements are from this immediate. + * Has the effect of making size-one immediates into scalars. + */ + for (j = nr; j < 4; j++) { + swizzle |= (swizzle & 0x3) << (j * 2); + } + + return ureg_swizzle(ureg_src_register(TGSI_FILE_IMMEDIATE, i), + (swizzle >> 0) & 0x3, + (swizzle >> 2) & 0x3, + (swizzle >> 4) & 0x3, + (swizzle >> 6) & 0x3); +} + + +struct ureg_src +ureg_DECL_immediate( struct ureg_program *ureg, + const float *v, + unsigned nr ) +{ + union { + float f[4]; + unsigned u[4]; + } fu; + unsigned int i; + + for (i = 0; i < nr; i++) { + fu.f[i] = v[i]; + } + + return decl_immediate(ureg, fu.u, nr, TGSI_IMM_FLOAT32); +} + + +struct ureg_src +ureg_DECL_immediate_uint( struct ureg_program *ureg, + const unsigned *v, + unsigned nr ) +{ + return decl_immediate(ureg, v, nr, TGSI_IMM_UINT32); +} + + +struct ureg_src +ureg_DECL_immediate_block_uint( struct ureg_program *ureg, + const unsigned *v, + unsigned nr ) +{ + uint index; + uint i; + + if (ureg->nr_immediates + (nr + 3) / 4 > UREG_MAX_IMMEDIATE) { + set_bad(ureg); + return ureg_src_register(TGSI_FILE_IMMEDIATE, 0); + } + + index = ureg->nr_immediates; + ureg->nr_immediates += (nr + 3) / 4; + + for (i = index; i < ureg->nr_immediates; i++) { + ureg->immediate[i].type = TGSI_IMM_UINT32; + ureg->immediate[i].nr = nr > 4 ? 4 : nr; + memcpy(ureg->immediate[i].value.u, + &v[(i - index) * 4], + ureg->immediate[i].nr * sizeof(uint)); + nr -= 4; + } + + return ureg_src_register(TGSI_FILE_IMMEDIATE, index); +} + + +struct ureg_src +ureg_DECL_immediate_int( struct ureg_program *ureg, + const int *v, + unsigned nr ) +{ + return decl_immediate(ureg, (const unsigned *)v, nr, TGSI_IMM_INT32); +} + + +void +ureg_emit_src( struct ureg_program *ureg, + struct ureg_src src ) +{ + unsigned size = 1 + (src.Indirect ? 1 : 0) + + (src.Dimension ? (src.DimIndirect ? 2 : 1) : 0); + + union tgsi_any_token *out = get_tokens( ureg, DOMAIN_INSN, size ); + unsigned n = 0; + + assert(src.File != TGSI_FILE_NULL); + assert(src.File < TGSI_FILE_COUNT); + + out[n].value = 0; + out[n].src.File = src.File; + out[n].src.SwizzleX = src.SwizzleX; + out[n].src.SwizzleY = src.SwizzleY; + out[n].src.SwizzleZ = src.SwizzleZ; + out[n].src.SwizzleW = src.SwizzleW; + out[n].src.Index = src.Index; + out[n].src.Negate = src.Negate; + out[0].src.Absolute = src.Absolute; + n++; + + if (src.Indirect) { + out[0].src.Indirect = 1; + out[n].value = 0; + out[n].ind.File = src.IndirectFile; + out[n].ind.Swizzle = src.IndirectSwizzle; + out[n].ind.Index = src.IndirectIndex; + out[n].ind.ArrayID = src.ArrayID; + n++; + } + + if (src.Dimension) { + out[0].src.Dimension = 1; + out[n].dim.Dimension = 0; + out[n].dim.Padding = 0; + if (src.DimIndirect) { + out[n].dim.Indirect = 1; + out[n].dim.Index = src.DimensionIndex; + n++; + out[n].value = 0; + out[n].ind.File = src.DimIndFile; + out[n].ind.Swizzle = src.DimIndSwizzle; + out[n].ind.Index = src.DimIndIndex; + out[n].ind.ArrayID = src.ArrayID; + } else { + out[n].dim.Indirect = 0; + out[n].dim.Index = src.DimensionIndex; + } + n++; + } + + assert(n == size); +} + + +void +ureg_emit_dst( struct ureg_program *ureg, + struct ureg_dst dst ) +{ + unsigned size = (1 + + (dst.Indirect ? 1 : 0)); + + union tgsi_any_token *out = get_tokens( ureg, DOMAIN_INSN, size ); + unsigned n = 0; + + assert(dst.File != TGSI_FILE_NULL); + assert(dst.File != TGSI_FILE_CONSTANT); + assert(dst.File != TGSI_FILE_INPUT); + assert(dst.File != TGSI_FILE_SAMPLER); + assert(dst.File != TGSI_FILE_SAMPLER_VIEW); + assert(dst.File != TGSI_FILE_IMMEDIATE); + assert(dst.File < TGSI_FILE_COUNT); + + out[n].value = 0; + out[n].dst.File = dst.File; + out[n].dst.WriteMask = dst.WriteMask; + out[n].dst.Indirect = dst.Indirect; + out[n].dst.Index = dst.Index; + n++; + + if (dst.Indirect) { + out[n].value = 0; + out[n].ind.File = dst.IndirectFile; + out[n].ind.Swizzle = dst.IndirectSwizzle; + out[n].ind.Index = dst.IndirectIndex; + out[n].ind.ArrayID = dst.ArrayID; + n++; + } + + assert(n == size); +} + + +static void validate( unsigned opcode, + unsigned nr_dst, + unsigned nr_src ) +{ +#ifdef DEBUG + const struct tgsi_opcode_info *info = tgsi_get_opcode_info( opcode ); + assert(info); + if(info) { + assert(nr_dst == info->num_dst); + assert(nr_src == info->num_src); + } +#else + (void)opcode; + (void)nr_dst; + (void)nr_src; +#endif +} + +struct ureg_emit_insn_result +ureg_emit_insn(struct ureg_program *ureg, + unsigned opcode, + boolean saturate, + unsigned precise, + unsigned num_dst, + unsigned num_src ) +{ + union tgsi_any_token *out; + uint count = 1; + struct ureg_emit_insn_result result; + + validate( opcode, num_dst, num_src ); + + out = get_tokens( ureg, DOMAIN_INSN, count ); + out[0].insn = tgsi_default_instruction(); + out[0].insn.Opcode = opcode; + out[0].insn.Saturate = saturate; + out[0].insn.Precise = precise; + out[0].insn.NumDstRegs = num_dst; + out[0].insn.NumSrcRegs = num_src; + + result.insn_token = ureg->domain[DOMAIN_INSN].count - count; + result.extended_token = result.insn_token; + + ureg->nr_instructions++; + + return result; +} + + +void +ureg_emit_label(struct ureg_program *ureg, + unsigned extended_token, + unsigned *label_token ) +{ + union tgsi_any_token *out, *insn; + + if(!label_token) + return; + + out = get_tokens( ureg, DOMAIN_INSN, 1 ); + out[0].value = 0; + + insn = retrieve_token( ureg, DOMAIN_INSN, extended_token ); + insn->insn.Label = 1; + + *label_token = ureg->domain[DOMAIN_INSN].count - 1; +} + +/* Will return a number which can be used in a label to point to the + * next instruction to be emitted. + */ +unsigned +ureg_get_instruction_number( struct ureg_program *ureg ) +{ + return ureg->nr_instructions; +} + +/* Patch a given label (expressed as a token number) to point to a + * given instruction (expressed as an instruction number). + */ +void +ureg_fixup_label(struct ureg_program *ureg, + unsigned label_token, + unsigned instruction_number ) +{ + union tgsi_any_token *out = retrieve_token( ureg, DOMAIN_INSN, label_token ); + + out->insn_label.Label = instruction_number; +} + + +void +ureg_emit_texture(struct ureg_program *ureg, + unsigned extended_token, + unsigned target, unsigned num_offsets) +{ + union tgsi_any_token *out, *insn; + + out = get_tokens( ureg, DOMAIN_INSN, 1 ); + insn = retrieve_token( ureg, DOMAIN_INSN, extended_token ); + + insn->insn.Texture = 1; + + out[0].value = 0; + out[0].insn_texture.Texture = target; + out[0].insn_texture.NumOffsets = num_offsets; +} + +void +ureg_emit_texture_offset(struct ureg_program *ureg, + const struct tgsi_texture_offset *offset) +{ + union tgsi_any_token *out; + + out = get_tokens( ureg, DOMAIN_INSN, 1); + + out[0].value = 0; + out[0].insn_texture_offset = *offset; + +} + + +void +ureg_fixup_insn_size(struct ureg_program *ureg, + unsigned insn ) +{ + union tgsi_any_token *out = retrieve_token( ureg, DOMAIN_INSN, insn ); + + assert(out->insn.Type == TGSI_TOKEN_TYPE_INSTRUCTION); + out->insn.NrTokens = ureg->domain[DOMAIN_INSN].count - insn - 1; +} + + +void +ureg_insn(struct ureg_program *ureg, + unsigned opcode, + const struct ureg_dst *dst, + unsigned nr_dst, + const struct ureg_src *src, + unsigned nr_src, + unsigned precise ) +{ + struct ureg_emit_insn_result insn; + unsigned i; + boolean saturate; + + if (nr_dst && ureg_dst_is_empty(dst[0])) { + return; + } + + saturate = nr_dst ? dst[0].Saturate : FALSE; + + insn = ureg_emit_insn(ureg, + opcode, + saturate, + precise, + nr_dst, + nr_src); + + for (i = 0; i < nr_dst; i++) + ureg_emit_dst( ureg, dst[i] ); + + for (i = 0; i < nr_src; i++) + ureg_emit_src( ureg, src[i] ); + + ureg_fixup_insn_size( ureg, insn.insn_token ); +} + +void +ureg_tex_insn(struct ureg_program *ureg, + unsigned opcode, + const struct ureg_dst *dst, + unsigned nr_dst, + unsigned target, + const struct tgsi_texture_offset *texoffsets, + unsigned nr_offset, + const struct ureg_src *src, + unsigned nr_src ) +{ + struct ureg_emit_insn_result insn; + unsigned i; + boolean saturate; + + if (nr_dst && ureg_dst_is_empty(dst[0])) { + return; + } + + saturate = nr_dst ? dst[0].Saturate : FALSE; + + insn = ureg_emit_insn(ureg, + opcode, + saturate, + 0, + nr_dst, + nr_src); + + ureg_emit_texture( ureg, insn.extended_token, target, nr_offset ); + + for (i = 0; i < nr_offset; i++) + ureg_emit_texture_offset( ureg, &texoffsets[i]); + + for (i = 0; i < nr_dst; i++) + ureg_emit_dst( ureg, dst[i] ); + + for (i = 0; i < nr_src; i++) + ureg_emit_src( ureg, src[i] ); + + ureg_fixup_insn_size( ureg, insn.insn_token ); +} + + +void +ureg_label_insn(struct ureg_program *ureg, + unsigned opcode, + const struct ureg_src *src, + unsigned nr_src, + unsigned *label_token ) +{ + struct ureg_emit_insn_result insn; + unsigned i; + + insn = ureg_emit_insn(ureg, + opcode, + FALSE, + 0, + 0, + nr_src); + + ureg_emit_label( ureg, insn.extended_token, label_token ); + + for (i = 0; i < nr_src; i++) + ureg_emit_src( ureg, src[i] ); + + ureg_fixup_insn_size( ureg, insn.insn_token ); +} + + +static void +emit_decl_semantic(struct ureg_program *ureg, + unsigned file, + unsigned index, + unsigned semantic_name, + unsigned semantic_index, + unsigned usage_mask) +{ + union tgsi_any_token *out = get_tokens(ureg, DOMAIN_DECL, 3); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 3; + out[0].decl.File = file; + out[0].decl.UsageMask = usage_mask; + out[0].decl.Semantic = 1; + + out[1].value = 0; + out[1].decl_range.First = index; + out[1].decl_range.Last = index; + + out[2].value = 0; + out[2].decl_semantic.Name = semantic_name; + out[2].decl_semantic.Index = semantic_index; +} + + +static void +emit_decl_fs(struct ureg_program *ureg, + unsigned file, + unsigned index, + unsigned semantic_name, + unsigned semantic_index, + unsigned interpolate, + unsigned cylindrical_wrap, + unsigned interpolate_location) +{ + union tgsi_any_token *out = get_tokens(ureg, DOMAIN_DECL, 4); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 4; + out[0].decl.File = file; + out[0].decl.UsageMask = TGSI_WRITEMASK_XYZW; /* FIXME! */ + out[0].decl.Interpolate = 1; + out[0].decl.Semantic = 1; + + out[1].value = 0; + out[1].decl_range.First = index; + out[1].decl_range.Last = index; + + out[2].value = 0; + out[2].decl_interp.Interpolate = interpolate; + out[2].decl_interp.CylindricalWrap = cylindrical_wrap; + out[2].decl_interp.Location = interpolate_location; + + out[3].value = 0; + out[3].decl_semantic.Name = semantic_name; + out[3].decl_semantic.Index = semantic_index; +} + +static void +emit_decl_temps( struct ureg_program *ureg, + unsigned first, unsigned last, + boolean local, + unsigned arrayid ) +{ + union tgsi_any_token *out = get_tokens( ureg, DOMAIN_DECL, + arrayid ? 3 : 2 ); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 2; + out[0].decl.File = TGSI_FILE_TEMPORARY; + out[0].decl.UsageMask = TGSI_WRITEMASK_XYZW; + out[0].decl.Local = local; + + out[1].value = 0; + out[1].decl_range.First = first; + out[1].decl_range.Last = last; + + if (arrayid) { + out[0].decl.Array = 1; + out[2].value = 0; + out[2].array.ArrayID = arrayid; + } +} + +static void emit_decl_range( struct ureg_program *ureg, + unsigned file, + unsigned first, + unsigned count ) +{ + union tgsi_any_token *out = get_tokens( ureg, DOMAIN_DECL, 2 ); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 2; + out[0].decl.File = file; + out[0].decl.UsageMask = TGSI_WRITEMASK_XYZW; + out[0].decl.Semantic = 0; + + out[1].value = 0; + out[1].decl_range.First = first; + out[1].decl_range.Last = first + count - 1; +} + +static void +emit_decl_range2D(struct ureg_program *ureg, + unsigned file, + unsigned first, + unsigned last, + unsigned index2D) +{ + union tgsi_any_token *out = get_tokens(ureg, DOMAIN_DECL, 3); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 3; + out[0].decl.File = file; + out[0].decl.UsageMask = TGSI_WRITEMASK_XYZW; + out[0].decl.Dimension = 1; + + out[1].value = 0; + out[1].decl_range.First = first; + out[1].decl_range.Last = last; + + out[2].value = 0; + out[2].decl_dim.Index2D = index2D; +} + +static void +emit_decl_sampler_view(struct ureg_program *ureg, + unsigned index, + unsigned target, + unsigned return_type_x, + unsigned return_type_y, + unsigned return_type_z, + unsigned return_type_w ) +{ + union tgsi_any_token *out = get_tokens(ureg, DOMAIN_DECL, 3); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 3; + out[0].decl.File = TGSI_FILE_SAMPLER_VIEW; + out[0].decl.UsageMask = 0xf; + + out[1].value = 0; + out[1].decl_range.First = index; + out[1].decl_range.Last = index; + + out[2].value = 0; + out[2].decl_sampler_view.Resource = target; + out[2].decl_sampler_view.ReturnTypeX = return_type_x; + out[2].decl_sampler_view.ReturnTypeY = return_type_y; + out[2].decl_sampler_view.ReturnTypeZ = return_type_z; + out[2].decl_sampler_view.ReturnTypeW = return_type_w; +} + +static void +emit_immediate( struct ureg_program *ureg, + const unsigned *v, + unsigned type ) +{ + union tgsi_any_token *out = get_tokens( ureg, DOMAIN_DECL, 5 ); + + out[0].value = 0; + out[0].imm.Type = TGSI_TOKEN_TYPE_IMMEDIATE; + out[0].imm.NrTokens = 5; + out[0].imm.DataType = type; + out[0].imm.Padding = 0; + + out[1].imm_data.Uint = v[0]; + out[2].imm_data.Uint = v[1]; + out[3].imm_data.Uint = v[2]; + out[4].imm_data.Uint = v[3]; +} + +static void +emit_property(struct ureg_program *ureg, + unsigned name, + unsigned data) +{ + union tgsi_any_token *out = get_tokens(ureg, DOMAIN_DECL, 2); + + out[0].value = 0; + out[0].prop.Type = TGSI_TOKEN_TYPE_PROPERTY; + out[0].prop.NrTokens = 2; + out[0].prop.PropertyName = name; + + out[1].prop_data.Data = data; +} + +static void +emit_decl_atomic_2d(struct ureg_program *ureg, + unsigned first, + unsigned last, + unsigned index2D, + unsigned array_id) +{ + union tgsi_any_token *out = get_tokens(ureg, DOMAIN_DECL, array_id ? 4 : 3); + + out[0].value = 0; + out[0].decl.Type = TGSI_TOKEN_TYPE_DECLARATION; + out[0].decl.NrTokens = 3; + out[0].decl.File = TGSI_FILE_HW_ATOMIC; + out[0].decl.UsageMask = TGSI_WRITEMASK_XYZW; + out[0].decl.Dimension = 1; + out[0].decl.Array = array_id != 0; + + out[1].value = 0; + out[1].decl_range.First = first; + out[1].decl_range.Last = last; + + out[2].value = 0; + out[2].decl_dim.Index2D = index2D; + + if (array_id) { + out[3].value = 0; + out[3].array.ArrayID = array_id; + } +} + +static void emit_decls( struct ureg_program *ureg ) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ureg->properties); i++) + if (ureg->properties[i] != ~0u) + emit_property(ureg, i, ureg->properties[i]); + + if (ureg->processor == TGSI_PROCESSOR_VERTEX) { + for (i = 0; i < UREG_MAX_INPUT; i++) { + if (ureg->vs_inputs[i/32] & (1 << (i%32))) { + emit_decl_range( ureg, TGSI_FILE_INPUT, i, 1 ); + } + } + } else if (ureg->processor == TGSI_PROCESSOR_FRAGMENT) { + for (i = 0; i < ureg->nr_fs_inputs; i++) { + emit_decl_fs(ureg, + TGSI_FILE_INPUT, + i, + ureg->fs_input[i].semantic_name, + ureg->fs_input[i].semantic_index, + ureg->fs_input[i].interp, + ureg->fs_input[i].cylindrical_wrap, + ureg->fs_input[i].interp_location); + } + } else { + for (i = 0; i < ureg->nr_gs_inputs; i++) { + emit_decl_semantic(ureg, + TGSI_FILE_INPUT, + ureg->gs_input[i].index, + ureg->gs_input[i].semantic_name, + ureg->gs_input[i].semantic_index, + TGSI_WRITEMASK_XYZW); + } + } + + for (i = 0; i < ureg->nr_system_values; i++) { + emit_decl_semantic(ureg, + TGSI_FILE_SYSTEM_VALUE, + ureg->system_value[i].index, + ureg->system_value[i].semantic_name, + ureg->system_value[i].semantic_index, + TGSI_WRITEMASK_XYZW); + } + + for (i = 0; i < ureg->nr_outputs; i++) { + emit_decl_semantic(ureg, + TGSI_FILE_OUTPUT, + i, + ureg->output[i].semantic_name, + ureg->output[i].semantic_index, + ureg->output[i].usage_mask); + } + + for (i = 0; i < ureg->nr_samplers; i++) { + emit_decl_range( ureg, + TGSI_FILE_SAMPLER, + ureg->sampler[i].Index, 1 ); + } + + for (i = 0; i < ureg->nr_sampler_views; i++) { + emit_decl_sampler_view(ureg, + ureg->sampler_view[i].index, + ureg->sampler_view[i].target, + ureg->sampler_view[i].return_type_x, + ureg->sampler_view[i].return_type_y, + ureg->sampler_view[i].return_type_z, + ureg->sampler_view[i].return_type_w); + } + + if (ureg->const_decls.nr_constant_ranges) { + for (i = 0; i < ureg->const_decls.nr_constant_ranges; i++) { + emit_decl_range(ureg, + TGSI_FILE_CONSTANT, + ureg->const_decls.constant_range[i].first, + ureg->const_decls.constant_range[i].last - ureg->const_decls.constant_range[i].first + 1); + } + } + + for (i = 0; i < PIPE_MAX_CONSTANT_BUFFERS; i++) { + struct const_decl *decl = &ureg->const_decls2D[i]; + + if (decl->nr_constant_ranges) { + uint j; + + for (j = 0; j < decl->nr_constant_ranges; j++) { + emit_decl_range2D(ureg, + TGSI_FILE_CONSTANT, + decl->constant_range[j].first, + decl->constant_range[j].last, + i); + } + } + } + + for (i = 0; i < PIPE_MAX_HW_ATOMIC_BUFFERS; i++) { + struct hw_atomic_decl *decl = &ureg->hw_atomic_decls[i]; + + if (decl->nr_hw_atomic_ranges) { + uint j; + + for (j = 0; j < decl->nr_hw_atomic_ranges; j++) { + emit_decl_atomic_2d(ureg, + decl->hw_atomic_range[j].first, + decl->hw_atomic_range[j].last, + i, + decl->hw_atomic_range[j].array_id); + } + } + } + + if (ureg->nr_temps) { + unsigned array = 0; + for (i = 0; i < ureg->nr_temps;) { + boolean local = util_bitmask_get(ureg->local_temps, i); + unsigned first = i; + i = util_bitmask_get_next_index(ureg->decl_temps, i + 1); + if (i == UTIL_BITMASK_INVALID_INDEX) + i = ureg->nr_temps; + + if (array < ureg->nr_array_temps && ureg->array_temps[array] == first) + emit_decl_temps( ureg, first, i - 1, local, ++array ); + else + emit_decl_temps( ureg, first, i - 1, local, 0 ); + } + } + + if (ureg->nr_addrs) { + emit_decl_range( ureg, + TGSI_FILE_ADDRESS, + 0, ureg->nr_addrs ); + } + + if (ureg->nr_preds) { + emit_decl_range(ureg, + TGSI_FILE_PREDICATE, + 0, + ureg->nr_preds); + } + + for (i = 0; i < ureg->nr_immediates; i++) { + emit_immediate( ureg, + ureg->immediate[i].value.u, + ureg->immediate[i].type ); + } +} + +/* Append the instruction tokens onto the declarations to build a + * contiguous stream suitable to send to the driver. + */ +static void copy_instructions( struct ureg_program *ureg ) +{ + unsigned nr_tokens = ureg->domain[DOMAIN_INSN].count; + union tgsi_any_token *out = get_tokens( ureg, + DOMAIN_DECL, + nr_tokens ); + + memcpy(out, + ureg->domain[DOMAIN_INSN].tokens, + nr_tokens * sizeof out[0] ); +} + +static void +fixup_header_size(struct ureg_program *ureg) +{ + union tgsi_any_token *out = retrieve_token( ureg, DOMAIN_DECL, 0 ); + + out->header.BodySize = ureg->domain[DOMAIN_DECL].count - 2; +} + + +static void +emit_header( struct ureg_program *ureg ) +{ + union tgsi_any_token *out = get_tokens( ureg, DOMAIN_DECL, 2 ); + + out[0].header.HeaderSize = 2; + out[0].header.BodySize = 0; + + out[1].processor.Processor = ureg->processor; + out[1].processor.Padding = 0; +} + + +const struct tgsi_token *ureg_finalize( struct ureg_program *ureg ) +{ + const struct tgsi_token *tokens; + + emit_header( ureg ); + emit_decls( ureg ); + copy_instructions( ureg ); + fixup_header_size( ureg ); + + if (ureg->domain[0].tokens == error_tokens || + ureg->domain[1].tokens == error_tokens) { + debug_printf("%s: error in generated shader\n", __FUNCTION__); + assert(0); + return NULL; + } + + tokens = &ureg->domain[DOMAIN_DECL].tokens[0].token; + + if (0) { + debug_printf("%s: emitted shader %d tokens:\n", __FUNCTION__, + ureg->domain[DOMAIN_DECL].count); + tgsi_dump( tokens, 0 ); + } + +#if DEBUG + if (tokens && !tgsi_sanity_check(tokens)) { + debug_printf("tgsi_ureg.c, sanity check failed on generated tokens:\n"); + tgsi_dump(tokens, 0); + assert(0); + } +#endif + + + return tokens; +} + + +void *ureg_create_shader( struct ureg_program *ureg, + struct pipe_context *pipe, + const struct pipe_stream_output_info *so ) +{ + struct pipe_shader_state state; + + state.tokens = ureg_finalize(ureg); + if(!state.tokens) + return NULL; + + if (so) + state.stream_output = *so; + else + memset(&state.stream_output, 0, sizeof(state.stream_output)); + + if (ureg->processor == TGSI_PROCESSOR_VERTEX) + return pipe->create_vs_state( pipe, &state ); + else + return pipe->create_fs_state( pipe, &state ); +} + + +const struct tgsi_token *ureg_get_tokens( struct ureg_program *ureg, + unsigned *nr_tokens ) +{ + const struct tgsi_token *tokens; + + ureg_finalize(ureg); + + tokens = &ureg->domain[DOMAIN_DECL].tokens[0].token; + + if (nr_tokens) + *nr_tokens = ureg->domain[DOMAIN_DECL].size; + + ureg->domain[DOMAIN_DECL].tokens = 0; + ureg->domain[DOMAIN_DECL].size = 0; + ureg->domain[DOMAIN_DECL].order = 0; + ureg->domain[DOMAIN_DECL].count = 0; + + return tokens; +} + + +void ureg_free_tokens( const struct tgsi_token *tokens ) +{ + FREE((struct tgsi_token *)tokens); +} + + +struct ureg_program *ureg_create( unsigned processor ) +{ + unsigned i; + struct ureg_program *ureg = CALLOC_STRUCT( ureg_program ); + if (ureg == NULL) + goto no_ureg; + + ureg->processor = processor; + + for (i = 0; i < ARRAY_SIZE(ureg->properties); i++) + ureg->properties[i] = ~0; + + ureg->free_temps = util_bitmask_create(); + if (ureg->free_temps == NULL) + goto no_free_temps; + + ureg->local_temps = util_bitmask_create(); + if (ureg->local_temps == NULL) + goto no_local_temps; + + ureg->decl_temps = util_bitmask_create(); + if (ureg->decl_temps == NULL) + goto no_decl_temps; + + return ureg; + +no_decl_temps: + util_bitmask_destroy(ureg->local_temps); +no_local_temps: + util_bitmask_destroy(ureg->free_temps); +no_free_temps: + FREE(ureg); +no_ureg: + return NULL; +} + + +unsigned +ureg_get_nr_outputs( const struct ureg_program *ureg ) +{ + if (!ureg) + return 0; + return ureg->nr_outputs; +} + + +void ureg_destroy( struct ureg_program *ureg ) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ureg->domain); i++) { + if (ureg->domain[i].tokens && + ureg->domain[i].tokens != error_tokens) + FREE(ureg->domain[i].tokens); + } + + util_bitmask_destroy(ureg->free_temps); + util_bitmask_destroy(ureg->local_temps); + util_bitmask_destroy(ureg->decl_temps); + + FREE(ureg); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_ureg.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_ureg.h new file mode 100644 index 0000000..a68af19 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_ureg.h @@ -0,0 +1,1206 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE, INC AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_UREG_H +#define TGSI_UREG_H + +#include "pipe/p_compiler.h" +#include "pipe/p_shader_tokens.h" +#include "util/u_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ureg_program; +struct pipe_stream_output_info; + +/* Almost a tgsi_src_register, but we need to pull in the Absolute + * flag from the _ext token. Indirect flag always implies ADDR[0]. + */ +struct ureg_src +{ + unsigned File : 4; /* TGSI_FILE_ */ + unsigned SwizzleX : 2; /* TGSI_SWIZZLE_ */ + unsigned SwizzleY : 2; /* TGSI_SWIZZLE_ */ + unsigned SwizzleZ : 2; /* TGSI_SWIZZLE_ */ + unsigned SwizzleW : 2; /* TGSI_SWIZZLE_ */ + unsigned Indirect : 1; /* BOOL */ + unsigned DimIndirect : 1; /* BOOL */ + unsigned Dimension : 1; /* BOOL */ + unsigned Absolute : 1; /* BOOL */ + unsigned Negate : 1; /* BOOL */ + unsigned IndirectFile : 4; /* TGSI_FILE_ */ + unsigned IndirectSwizzle : 2; /* TGSI_SWIZZLE_ */ + unsigned DimIndFile : 4; /* TGSI_FILE_ */ + unsigned DimIndSwizzle : 2; /* TGSI_SWIZZLE_ */ + int Index : 16; /* SINT */ + int IndirectIndex : 16; /* SINT */ + int DimensionIndex : 16; /* SINT */ + int DimIndIndex : 16; /* SINT */ + unsigned ArrayID : 10; /* UINT */ +}; + +/* Very similar to a tgsi_dst_register, removing unsupported fields + * and adding a Saturate flag. It's easier to push saturate into the + * destination register than to try and create a _SAT variant of each + * instruction function. + */ +struct ureg_dst +{ + unsigned File : 4; /* TGSI_FILE_ */ + unsigned WriteMask : 4; /* TGSI_WRITEMASK_ */ + unsigned Indirect : 1; /* BOOL */ + unsigned Saturate : 1; /* BOOL */ + unsigned Predicate : 1; + unsigned PredNegate : 1; /* BOOL */ + unsigned PredSwizzleX : 2; /* TGSI_SWIZZLE_ */ + unsigned PredSwizzleY : 2; /* TGSI_SWIZZLE_ */ + unsigned PredSwizzleZ : 2; /* TGSI_SWIZZLE_ */ + unsigned PredSwizzleW : 2; /* TGSI_SWIZZLE_ */ + int Index : 16; /* SINT */ + int IndirectIndex : 16; /* SINT */ + unsigned IndirectFile : 4; /* TGSI_FILE_ */ + int IndirectSwizzle : 2; /* TGSI_SWIZZLE_ */ + unsigned ArrayID : 10; /* UINT */ +}; + +struct pipe_context; + +struct ureg_program * +ureg_create( unsigned processor ); + +const struct tgsi_token * +ureg_finalize( struct ureg_program * ); + +/* Create and return a shader: + */ +void * +ureg_create_shader( struct ureg_program *, + struct pipe_context *pipe, + const struct pipe_stream_output_info *so ); + + +/* Alternately, return the built token stream and hand ownership of + * that memory to the caller: + */ +const struct tgsi_token * +ureg_get_tokens( struct ureg_program *ureg, + unsigned *nr_tokens ); + +/* + * Returns the number of currently declared outputs. + */ +unsigned +ureg_get_nr_outputs( const struct ureg_program *ureg ); + + +/* Free the tokens created by ureg_get_tokens() */ +void ureg_free_tokens( const struct tgsi_token *tokens ); + + +void +ureg_destroy( struct ureg_program * ); + + +/*********************************************************************** + * Convenience routine: + */ +static inline void * +ureg_create_shader_with_so_and_destroy( struct ureg_program *p, + struct pipe_context *pipe, + const struct pipe_stream_output_info *so ) +{ + void *result = ureg_create_shader( p, pipe, so ); + ureg_destroy( p ); + return result; +} + +static inline void * +ureg_create_shader_and_destroy( struct ureg_program *p, + struct pipe_context *pipe ) +{ + return ureg_create_shader_with_so_and_destroy(p, pipe, NULL); +} + + +/*********************************************************************** + * Build shader properties: + */ + +void +ureg_property(struct ureg_program *ureg, unsigned name, unsigned value); + + +/*********************************************************************** + * Build shader declarations: + */ + +struct ureg_src +ureg_DECL_fs_input_cyl_centroid(struct ureg_program *, + unsigned semantic_name, + unsigned semantic_index, + unsigned interp_mode, + unsigned cylindrical_wrap, + unsigned interp_location); + +static inline struct ureg_src +ureg_DECL_fs_input_cyl(struct ureg_program *ureg, + unsigned semantic_name, + unsigned semantic_index, + unsigned interp_mode, + unsigned cylindrical_wrap) +{ + return ureg_DECL_fs_input_cyl_centroid(ureg, + semantic_name, + semantic_index, + interp_mode, + cylindrical_wrap, + 0); +} + +static inline struct ureg_src +ureg_DECL_fs_input(struct ureg_program *ureg, + unsigned semantic_name, + unsigned semantic_index, + unsigned interp_mode) +{ + return ureg_DECL_fs_input_cyl_centroid(ureg, + semantic_name, + semantic_index, + interp_mode, + 0, 0); +} + +struct ureg_src +ureg_DECL_vs_input( struct ureg_program *, + unsigned index ); + +struct ureg_src +ureg_DECL_gs_input(struct ureg_program *, + unsigned index, + unsigned semantic_name, + unsigned semantic_index); + +struct ureg_src +ureg_DECL_system_value(struct ureg_program *, + unsigned index, + unsigned semantic_name, + unsigned semantic_index); + +struct ureg_dst +ureg_DECL_output_masked( struct ureg_program *, + unsigned semantic_name, + unsigned semantic_index, + unsigned usage_mask ); + +struct ureg_dst +ureg_DECL_output( struct ureg_program *, + unsigned semantic_name, + unsigned semantic_index ); + +struct ureg_src +ureg_DECL_immediate( struct ureg_program *, + const float *v, + unsigned nr ); + +struct ureg_src +ureg_DECL_immediate_uint( struct ureg_program *, + const unsigned *v, + unsigned nr ); + +struct ureg_src +ureg_DECL_immediate_block_uint( struct ureg_program *, + const unsigned *v, + unsigned nr ); + +struct ureg_src +ureg_DECL_immediate_int( struct ureg_program *, + const int *v, + unsigned nr ); + +void +ureg_DECL_constant2D(struct ureg_program *ureg, + unsigned first, + unsigned last, + unsigned index2D); + +struct ureg_src +ureg_DECL_constant( struct ureg_program *, + unsigned index ); + +void +ureg_DECL_hw_atomic(struct ureg_program *ureg, + unsigned first, + unsigned last, + unsigned buffer_id, + unsigned array_id); + +struct ureg_dst +ureg_DECL_temporary( struct ureg_program * ); + +/** + * Emit a temporary with the LOCAL declaration flag set. For use when + * the register value is not required to be preserved across + * subroutine boundaries. + */ +struct ureg_dst +ureg_DECL_local_temporary( struct ureg_program * ); + +/** + * Declare "size" continuous temporary registers. + */ +struct ureg_dst +ureg_DECL_array_temporary( struct ureg_program *, + unsigned size, + boolean local ); + +void +ureg_release_temporary( struct ureg_program *ureg, + struct ureg_dst tmp ); + +struct ureg_dst +ureg_DECL_address( struct ureg_program * ); + +struct ureg_dst +ureg_DECL_predicate(struct ureg_program *); + +/* Supply an index to the sampler declaration as this is the hook to + * the external pipe_sampler state. Users of this function probably + * don't want just any sampler, but a specific one which they've set + * up state for in the context. + */ +struct ureg_src +ureg_DECL_sampler(struct ureg_program *, + int index ); + +struct ureg_src +ureg_DECL_sampler_view(struct ureg_program *, + unsigned index, + unsigned target, + unsigned return_type_x, + unsigned return_type_y, + unsigned return_type_z, + unsigned return_type_w ); + + +static inline struct ureg_src +ureg_imm4f( struct ureg_program *ureg, + float a, float b, + float c, float d) +{ + float v[4]; + v[0] = a; + v[1] = b; + v[2] = c; + v[3] = d; + return ureg_DECL_immediate( ureg, v, 4 ); +} + +static inline struct ureg_src +ureg_imm3f( struct ureg_program *ureg, + float a, float b, + float c) +{ + float v[3]; + v[0] = a; + v[1] = b; + v[2] = c; + return ureg_DECL_immediate( ureg, v, 3 ); +} + +static inline struct ureg_src +ureg_imm2f( struct ureg_program *ureg, + float a, float b) +{ + float v[2]; + v[0] = a; + v[1] = b; + return ureg_DECL_immediate( ureg, v, 2 ); +} + +static inline struct ureg_src +ureg_imm1f( struct ureg_program *ureg, + float a) +{ + float v[1]; + v[0] = a; + return ureg_DECL_immediate( ureg, v, 1 ); +} + +static inline struct ureg_src +ureg_imm4u( struct ureg_program *ureg, + unsigned a, unsigned b, + unsigned c, unsigned d) +{ + unsigned v[4]; + v[0] = a; + v[1] = b; + v[2] = c; + v[3] = d; + return ureg_DECL_immediate_uint( ureg, v, 4 ); +} + +static inline struct ureg_src +ureg_imm3u( struct ureg_program *ureg, + unsigned a, unsigned b, + unsigned c) +{ + unsigned v[3]; + v[0] = a; + v[1] = b; + v[2] = c; + return ureg_DECL_immediate_uint( ureg, v, 3 ); +} + +static inline struct ureg_src +ureg_imm2u( struct ureg_program *ureg, + unsigned a, unsigned b) +{ + unsigned v[2]; + v[0] = a; + v[1] = b; + return ureg_DECL_immediate_uint( ureg, v, 2 ); +} + +static inline struct ureg_src +ureg_imm1u( struct ureg_program *ureg, + unsigned a) +{ + return ureg_DECL_immediate_uint( ureg, &a, 1 ); +} + +static inline struct ureg_src +ureg_imm4i( struct ureg_program *ureg, + int a, int b, + int c, int d) +{ + int v[4]; + v[0] = a; + v[1] = b; + v[2] = c; + v[3] = d; + return ureg_DECL_immediate_int( ureg, v, 4 ); +} + +static inline struct ureg_src +ureg_imm3i( struct ureg_program *ureg, + int a, int b, + int c) +{ + int v[3]; + v[0] = a; + v[1] = b; + v[2] = c; + return ureg_DECL_immediate_int( ureg, v, 3 ); +} + +static inline struct ureg_src +ureg_imm2i( struct ureg_program *ureg, + int a, int b) +{ + int v[2]; + v[0] = a; + v[1] = b; + return ureg_DECL_immediate_int( ureg, v, 2 ); +} + +static inline struct ureg_src +ureg_imm1i( struct ureg_program *ureg, + int a) +{ + return ureg_DECL_immediate_int( ureg, &a, 1 ); +} + +/* Where the destination register has a valid file, but an empty + * writemask. + */ +static inline boolean +ureg_dst_is_empty( struct ureg_dst dst ) +{ + return dst.File != TGSI_FILE_NULL && + dst.WriteMask == 0; +} + +/*********************************************************************** + * Functions for patching up labels + */ + + +/* Will return a number which can be used in a label to point to the + * next instruction to be emitted. + */ +unsigned +ureg_get_instruction_number( struct ureg_program *ureg ); + + +/* Patch a given label (expressed as a token number) to point to a + * given instruction (expressed as an instruction number). + * + * Labels are obtained from instruction emitters, eg ureg_CAL(). + * Instruction numbers are obtained from ureg_get_instruction_number(), + * above. + */ +void +ureg_fixup_label(struct ureg_program *ureg, + unsigned label_token, + unsigned instruction_number ); + + +/* Generic instruction emitter. Use if you need to pass the opcode as + * a parameter, rather than using the emit_OP() variants below. + */ +void +ureg_insn(struct ureg_program *ureg, + unsigned opcode, + const struct ureg_dst *dst, + unsigned nr_dst, + const struct ureg_src *src, + unsigned nr_src, + unsigned precise ); + + +void +ureg_tex_insn(struct ureg_program *ureg, + unsigned opcode, + const struct ureg_dst *dst, + unsigned nr_dst, + unsigned target, + const struct tgsi_texture_offset *texoffsets, + unsigned nr_offset, + const struct ureg_src *src, + unsigned nr_src ); + + +void +ureg_label_insn(struct ureg_program *ureg, + unsigned opcode, + const struct ureg_src *src, + unsigned nr_src, + unsigned *label); + + +/*********************************************************************** + * Internal instruction helpers, don't call these directly: + */ + +struct ureg_emit_insn_result { + unsigned insn_token; /*< Used to fixup insn size. */ + unsigned extended_token; /*< Used to set the Extended bit, usually the same as insn_token. */ +}; + +struct ureg_emit_insn_result +ureg_emit_insn(struct ureg_program *ureg, + unsigned opcode, + boolean saturate, + unsigned precise, + unsigned num_dst, + unsigned num_src ); + +void +ureg_emit_label(struct ureg_program *ureg, + unsigned insn_token, + unsigned *label_token ); + +void +ureg_emit_texture(struct ureg_program *ureg, + unsigned insn_token, + unsigned target, unsigned num_offsets); + +void +ureg_emit_texture_offset(struct ureg_program *ureg, + const struct tgsi_texture_offset *offset); + +void +ureg_emit_dst( struct ureg_program *ureg, + struct ureg_dst dst ); + +void +ureg_emit_src( struct ureg_program *ureg, + struct ureg_src src ); + +void +ureg_fixup_insn_size(struct ureg_program *ureg, + unsigned insn ); + + +#define OP00( op ) \ +static inline void ureg_##op( struct ureg_program *ureg ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + FALSE, \ + 0, \ + 0, \ + 0); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP01( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_src src ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + FALSE, \ + 0, \ + 0, \ + 1); \ + ureg_emit_src( ureg, src ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP00_LBL( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + unsigned *label_token ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + FALSE, \ + 0, \ + 0, \ + 0); \ + ureg_emit_label( ureg, insn.extended_token, label_token ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP01_LBL( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_src src, \ + unsigned *label_token ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + FALSE, \ + 0, \ + 0, \ + 1); \ + ureg_emit_label( ureg, insn.extended_token, label_token ); \ + ureg_emit_src( ureg, src ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP10( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 0); \ + ureg_emit_dst( ureg, dst ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + + +#define OP11( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 1); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP12( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 2); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP12_TEX( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + unsigned target, \ + struct ureg_src src0, \ + struct ureg_src src1 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 2); \ + ureg_emit_texture( ureg, insn.extended_token, target, 0 ); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP12_SAMPLE( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + unsigned target = TGSI_TEXTURE_UNKNOWN; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 2); \ + ureg_emit_texture( ureg, insn.extended_token, target, 0 ); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP13( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 3); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP13_SAMPLE( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + unsigned target = TGSI_TEXTURE_UNKNOWN; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 3); \ + ureg_emit_texture( ureg, insn.extended_token, target, 0 ); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP14_TEX( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + unsigned target, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2, \ + struct ureg_src src3 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 4); \ + ureg_emit_texture( ureg, insn.extended_token, target, 0 ); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_emit_src( ureg, src3 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP14_SAMPLE( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2, \ + struct ureg_src src3 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + unsigned target = TGSI_TEXTURE_UNKNOWN; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 4); \ + ureg_emit_texture( ureg, insn.extended_token, target, 0 ); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_emit_src( ureg, src3 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + + +#define OP14( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2, \ + struct ureg_src src3 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 4); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_emit_src( ureg, src3 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + + +#define OP15( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2, \ + struct ureg_src src3, \ + struct ureg_src src4 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 5); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_emit_src( ureg, src3 ); \ + ureg_emit_src( ureg, src4 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +#define OP15_SAMPLE( op ) \ +static inline void ureg_##op( struct ureg_program *ureg, \ + struct ureg_dst dst, \ + struct ureg_src src0, \ + struct ureg_src src1, \ + struct ureg_src src2, \ + struct ureg_src src3, \ + struct ureg_src src4 ) \ +{ \ + unsigned opcode = TGSI_OPCODE_##op; \ + unsigned target = TGSI_TEXTURE_UNKNOWN; \ + struct ureg_emit_insn_result insn; \ + if (ureg_dst_is_empty(dst)) \ + return; \ + insn = ureg_emit_insn(ureg, \ + opcode, \ + dst.Saturate, \ + 0, \ + 1, \ + 5); \ + ureg_emit_texture( ureg, insn.extended_token, target, 0 ); \ + ureg_emit_dst( ureg, dst ); \ + ureg_emit_src( ureg, src0 ); \ + ureg_emit_src( ureg, src1 ); \ + ureg_emit_src( ureg, src2 ); \ + ureg_emit_src( ureg, src3 ); \ + ureg_emit_src( ureg, src4 ); \ + ureg_fixup_insn_size( ureg, insn.insn_token ); \ +} + +/* Use a template include to generate a correctly-typed ureg_OP() + * function for each TGSI opcode: + */ +#include "tgsi_opcode_tmp.h" + + +/*********************************************************************** + * Inline helpers for manipulating register structs: + */ +static inline struct ureg_src +ureg_negate( struct ureg_src reg ) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.Negate ^= 1; + return reg; +} + +static inline struct ureg_src +ureg_abs( struct ureg_src reg ) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.Absolute = 1; + reg.Negate = 0; + return reg; +} + +static inline struct ureg_src +ureg_swizzle( struct ureg_src reg, + int x, int y, int z, int w ) +{ + unsigned swz = ( (reg.SwizzleX << 0) | + (reg.SwizzleY << 2) | + (reg.SwizzleZ << 4) | + (reg.SwizzleW << 6)); + + assert(reg.File != TGSI_FILE_NULL); + assert(x < 4); + assert(y < 4); + assert(z < 4); + assert(w < 4); + + reg.SwizzleX = (swz >> (x*2)) & 0x3; + reg.SwizzleY = (swz >> (y*2)) & 0x3; + reg.SwizzleZ = (swz >> (z*2)) & 0x3; + reg.SwizzleW = (swz >> (w*2)) & 0x3; + return reg; +} + +static inline struct ureg_src +ureg_scalar( struct ureg_src reg, int x ) +{ + return ureg_swizzle(reg, x, x, x, x); +} + +static inline struct ureg_dst +ureg_writemask( struct ureg_dst reg, + unsigned writemask ) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.WriteMask &= writemask; + return reg; +} + +static inline struct ureg_dst +ureg_saturate( struct ureg_dst reg ) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.Saturate = 1; + return reg; +} + +static inline struct ureg_dst +ureg_predicate(struct ureg_dst reg, + boolean negate, + unsigned swizzle_x, + unsigned swizzle_y, + unsigned swizzle_z, + unsigned swizzle_w) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.Predicate = 1; + reg.PredNegate = negate; + reg.PredSwizzleX = swizzle_x; + reg.PredSwizzleY = swizzle_y; + reg.PredSwizzleZ = swizzle_z; + reg.PredSwizzleW = swizzle_w; + return reg; +} + +static inline struct ureg_dst +ureg_dst_indirect( struct ureg_dst reg, struct ureg_src addr ) +{ + assert(reg.File != TGSI_FILE_NULL); + assert(addr.File == TGSI_FILE_ADDRESS || addr.File == TGSI_FILE_TEMPORARY); + reg.Indirect = 1; + reg.IndirectFile = addr.File; + reg.IndirectIndex = addr.Index; + reg.IndirectSwizzle = addr.SwizzleX; + return reg; +} + +static inline struct ureg_src +ureg_src_indirect( struct ureg_src reg, struct ureg_src addr ) +{ + assert(reg.File != TGSI_FILE_NULL); + assert(addr.File == TGSI_FILE_ADDRESS || addr.File == TGSI_FILE_TEMPORARY); + reg.Indirect = 1; + reg.IndirectFile = addr.File; + reg.IndirectIndex = addr.Index; + reg.IndirectSwizzle = addr.SwizzleX; + return reg; +} + +static inline struct ureg_src +ureg_src_dimension( struct ureg_src reg, int index ) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.Dimension = 1; + reg.DimIndirect = 0; + reg.DimensionIndex = index; + return reg; +} + + +static inline struct ureg_src +ureg_src_dimension_indirect( struct ureg_src reg, struct ureg_src addr, + int index ) +{ + assert(reg.File != TGSI_FILE_NULL); + reg.Dimension = 1; + reg.DimIndirect = 1; + reg.DimensionIndex = index; + reg.DimIndFile = addr.File; + reg.DimIndIndex = addr.Index; + reg.DimIndSwizzle = addr.SwizzleX; + return reg; +} + +static inline struct ureg_dst +ureg_dst_array_offset( struct ureg_dst reg, int offset ) +{ + assert(reg.File == TGSI_FILE_TEMPORARY); + reg.Index += offset; + return reg; +} + +static inline struct ureg_dst +ureg_dst( struct ureg_src src ) +{ + struct ureg_dst dst; + + assert(!src.Indirect || + (src.IndirectFile == TGSI_FILE_ADDRESS || + src.IndirectFile == TGSI_FILE_TEMPORARY)); + + dst.File = src.File; + dst.WriteMask = TGSI_WRITEMASK_XYZW; + dst.IndirectFile = src.IndirectFile; + dst.Indirect = src.Indirect; + dst.IndirectIndex = src.IndirectIndex; + dst.IndirectSwizzle = src.IndirectSwizzle; + dst.Saturate = 0; + dst.Index = src.Index; + dst.ArrayID = src.ArrayID; + + return dst; +} + +static inline struct ureg_src +ureg_src_register(unsigned file, + unsigned index) +{ + struct ureg_src src; + + src.File = file; + src.SwizzleX = TGSI_SWIZZLE_X; + src.SwizzleY = TGSI_SWIZZLE_Y; + src.SwizzleZ = TGSI_SWIZZLE_Z; + src.SwizzleW = TGSI_SWIZZLE_W; + src.Indirect = 0; + src.IndirectFile = TGSI_FILE_NULL; + src.IndirectIndex = 0; + src.IndirectSwizzle = 0; + src.Absolute = 0; + src.Index = index; + src.Negate = 0; + src.Dimension = 0; + src.DimensionIndex = 0; + src.DimIndirect = 0; + src.DimIndFile = TGSI_FILE_NULL; + src.DimIndIndex = 0; + src.DimIndSwizzle = 0; + src.ArrayID = 0; + + return src; +} + +static inline struct ureg_src +ureg_src( struct ureg_dst dst ) +{ + struct ureg_src src; + + src.File = dst.File; + src.SwizzleX = TGSI_SWIZZLE_X; + src.SwizzleY = TGSI_SWIZZLE_Y; + src.SwizzleZ = TGSI_SWIZZLE_Z; + src.SwizzleW = TGSI_SWIZZLE_W; + src.Indirect = dst.Indirect; + src.IndirectFile = dst.IndirectFile; + src.IndirectIndex = dst.IndirectIndex; + src.IndirectSwizzle = dst.IndirectSwizzle; + src.Absolute = 0; + src.Index = dst.Index; + src.Negate = 0; + src.Dimension = 0; + src.DimensionIndex = 0; + src.DimIndirect = 0; + src.DimIndFile = TGSI_FILE_NULL; + src.DimIndIndex = 0; + src.DimIndSwizzle = 0; + src.ArrayID = dst.ArrayID; + + return src; +} + + + +static inline struct ureg_dst +ureg_dst_undef( void ) +{ + struct ureg_dst dst; + + dst.File = TGSI_FILE_NULL; + dst.WriteMask = 0; + dst.Indirect = 0; + dst.IndirectFile = TGSI_FILE_NULL; + dst.IndirectIndex = 0; + dst.IndirectSwizzle = 0; + dst.Saturate = 0; + dst.Index = 0; + dst.ArrayID = 0; + + return dst; +} + +static inline struct ureg_src +ureg_src_undef( void ) +{ + struct ureg_src src; + + src.File = TGSI_FILE_NULL; + src.SwizzleX = 0; + src.SwizzleY = 0; + src.SwizzleZ = 0; + src.SwizzleW = 0; + src.Indirect = 0; + src.IndirectFile = TGSI_FILE_NULL; + src.IndirectIndex = 0; + src.IndirectSwizzle = 0; + src.Absolute = 0; + src.Index = 0; + src.Negate = 0; + src.Dimension = 0; + src.DimensionIndex = 0; + src.DimIndirect = 0; + src.DimIndFile = TGSI_FILE_NULL; + src.DimIndIndex = 0; + src.DimIndSwizzle = 0; + src.ArrayID = 0; + + return src; +} + +static inline boolean +ureg_src_is_undef( struct ureg_src src ) +{ + return src.File == TGSI_FILE_NULL; +} + +static inline boolean +ureg_dst_is_undef( struct ureg_dst dst ) +{ + return dst.File == TGSI_FILE_NULL; +} + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_util.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_util.c new file mode 100644 index 0000000..48cdbb0 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_util.c @@ -0,0 +1,457 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_debug.h" +#include "pipe/p_shader_tokens.h" +#include "tgsi_parse.h" +#include "tgsi_util.h" + +union pointer_hack +{ + void *pointer; + uint64_t uint64; +}; + +void * +tgsi_align_128bit( + void *unaligned ) +{ + union pointer_hack ph; + + ph.uint64 = 0; + ph.pointer = unaligned; + ph.uint64 = (ph.uint64 + 15) & ~15; + return ph.pointer; +} + +unsigned +tgsi_util_get_src_register_swizzle( + const struct tgsi_src_register *reg, + unsigned component ) +{ + switch( component ) { + case 0: + return reg->SwizzleX; + case 1: + return reg->SwizzleY; + case 2: + return reg->SwizzleZ; + case 3: + return reg->SwizzleW; + default: + assert( 0 ); + } + return 0; +} + + +unsigned +tgsi_util_get_full_src_register_swizzle( + const struct tgsi_full_src_register *reg, + unsigned component ) +{ + return tgsi_util_get_src_register_swizzle( + ®->Register, + component ); +} + +void +tgsi_util_set_src_register_swizzle( + struct tgsi_src_register *reg, + unsigned swizzle, + unsigned component ) +{ + switch( component ) { + case 0: + reg->SwizzleX = swizzle; + break; + case 1: + reg->SwizzleY = swizzle; + break; + case 2: + reg->SwizzleZ = swizzle; + break; + case 3: + reg->SwizzleW = swizzle; + break; + default: + assert( 0 ); + } +} + +unsigned +tgsi_util_get_full_src_register_sign_mode( + const struct tgsi_full_src_register *reg, + UNUSED unsigned component ) +{ + unsigned sign_mode; + + if( reg->Register.Absolute ) { + /* Consider only the post-abs negation. */ + + if( reg->Register.Negate ) { + sign_mode = TGSI_UTIL_SIGN_SET; + } + else { + sign_mode = TGSI_UTIL_SIGN_CLEAR; + } + } + else { + if( reg->Register.Negate ) { + sign_mode = TGSI_UTIL_SIGN_TOGGLE; + } + else { + sign_mode = TGSI_UTIL_SIGN_KEEP; + } + } + + return sign_mode; +} + +void +tgsi_util_set_full_src_register_sign_mode( + struct tgsi_full_src_register *reg, + unsigned sign_mode ) +{ + switch (sign_mode) + { + case TGSI_UTIL_SIGN_CLEAR: + reg->Register.Negate = 0; + reg->Register.Absolute = 1; + break; + + case TGSI_UTIL_SIGN_SET: + reg->Register.Absolute = 1; + reg->Register.Negate = 1; + break; + + case TGSI_UTIL_SIGN_TOGGLE: + reg->Register.Negate = 1; + reg->Register.Absolute = 0; + break; + + case TGSI_UTIL_SIGN_KEEP: + reg->Register.Negate = 0; + reg->Register.Absolute = 0; + break; + + default: + assert( 0 ); + } +} + +/** + * Determine which channels of the specificed src register are effectively + * used by this instruction. + */ +unsigned +tgsi_util_get_inst_usage_mask(const struct tgsi_full_instruction *inst, + unsigned src_idx) +{ + const struct tgsi_full_src_register *src = &inst->Src[src_idx]; + unsigned write_mask = inst->Dst[0].Register.WriteMask; + unsigned read_mask; + unsigned usage_mask; + unsigned chan; + + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_MOV: + case TGSI_OPCODE_ARL: + case TGSI_OPCODE_ARR: + case TGSI_OPCODE_RCP: + case TGSI_OPCODE_MUL: + case TGSI_OPCODE_DIV: + case TGSI_OPCODE_ADD: + case TGSI_OPCODE_MIN: + case TGSI_OPCODE_MAX: + case TGSI_OPCODE_SLT: + case TGSI_OPCODE_SGE: + case TGSI_OPCODE_MAD: + case TGSI_OPCODE_SUB: + case TGSI_OPCODE_LRP: + case TGSI_OPCODE_FRC: + case TGSI_OPCODE_CEIL: + case TGSI_OPCODE_FLR: + case TGSI_OPCODE_ROUND: + case TGSI_OPCODE_POW: + case TGSI_OPCODE_ABS: + case TGSI_OPCODE_COS: + case TGSI_OPCODE_SIN: + case TGSI_OPCODE_DDX: + case TGSI_OPCODE_DDY: + case TGSI_OPCODE_SEQ: + case TGSI_OPCODE_SGT: + case TGSI_OPCODE_SLE: + case TGSI_OPCODE_SNE: + case TGSI_OPCODE_SSG: + case TGSI_OPCODE_CMP: + case TGSI_OPCODE_TRUNC: + case TGSI_OPCODE_NOT: + case TGSI_OPCODE_AND: + case TGSI_OPCODE_OR: + case TGSI_OPCODE_XOR: + case TGSI_OPCODE_FSEQ: + case TGSI_OPCODE_FSGE: + case TGSI_OPCODE_FSLT: + case TGSI_OPCODE_FSNE: + case TGSI_OPCODE_F2I: + case TGSI_OPCODE_IDIV: + case TGSI_OPCODE_IMAX: + case TGSI_OPCODE_IMIN: + case TGSI_OPCODE_INEG: + case TGSI_OPCODE_ISGE: + case TGSI_OPCODE_ISHR: + case TGSI_OPCODE_ISLT: + case TGSI_OPCODE_F2U: + case TGSI_OPCODE_U2F: + case TGSI_OPCODE_UADD: + case TGSI_OPCODE_UDIV: + case TGSI_OPCODE_UMAD: + case TGSI_OPCODE_UMAX: + case TGSI_OPCODE_UMIN: + case TGSI_OPCODE_UMOD: + case TGSI_OPCODE_UMUL: + case TGSI_OPCODE_USEQ: + case TGSI_OPCODE_USGE: + case TGSI_OPCODE_USHR: + case TGSI_OPCODE_USLT: + case TGSI_OPCODE_USNE: + case TGSI_OPCODE_IMUL_HI: + case TGSI_OPCODE_UMUL_HI: + case TGSI_OPCODE_DDX_FINE: + case TGSI_OPCODE_DDY_FINE: + /* Channel-wise operations */ + read_mask = write_mask; + break; + + case TGSI_OPCODE_EX2: + case TGSI_OPCODE_LG2: + read_mask = TGSI_WRITEMASK_X; + break; + + case TGSI_OPCODE_SCS: + read_mask = write_mask & TGSI_WRITEMASK_XY ? TGSI_WRITEMASK_X : 0; + break; + + case TGSI_OPCODE_EXP: + case TGSI_OPCODE_LOG: + read_mask = write_mask & TGSI_WRITEMASK_XYZ ? TGSI_WRITEMASK_X : 0; + break; + + case TGSI_OPCODE_DP2: + read_mask = TGSI_WRITEMASK_XY; + break; + + case TGSI_OPCODE_DP3: + read_mask = TGSI_WRITEMASK_XYZ; + break; + + case TGSI_OPCODE_DP4: + read_mask = TGSI_WRITEMASK_XYZW; + break; + + case TGSI_OPCODE_DPH: + read_mask = src_idx == 0 ? TGSI_WRITEMASK_XYZ : TGSI_WRITEMASK_XYZW; + break; + + case TGSI_OPCODE_TEX: + case TGSI_OPCODE_TXD: + case TGSI_OPCODE_TXB: + case TGSI_OPCODE_TXL: + case TGSI_OPCODE_TXP: + if (src_idx == 0) { + /* Note that the SHADOW variants use the Z component too */ + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + read_mask = TGSI_WRITEMASK_X; + break; + case TGSI_TEXTURE_SHADOW1D: + read_mask = TGSI_WRITEMASK_XZ; + break; + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_RECT: + read_mask = TGSI_WRITEMASK_XY; + break; + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_2D_MSAA: + read_mask = TGSI_WRITEMASK_XYZ; + break; + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + read_mask = TGSI_WRITEMASK_XYZW; + break; + default: + assert(0); + read_mask = 0; + } + + if (inst->Instruction.Opcode != TGSI_OPCODE_TEX) { + read_mask |= TGSI_WRITEMASK_W; + } + } else { + /* A safe approximation */ + read_mask = TGSI_WRITEMASK_XYZW; + } + break; + + default: + /* Assume all channels are read */ + read_mask = TGSI_WRITEMASK_XYZW; + break; + } + + usage_mask = 0; + for (chan = 0; chan < 4; ++chan) { + if (read_mask & (1 << chan)) { + usage_mask |= 1 << tgsi_util_get_full_src_register_swizzle(src, chan); + } + } + + return usage_mask; +} + +/** + * Convert a tgsi_ind_register into a tgsi_src_register + */ +struct tgsi_src_register +tgsi_util_get_src_from_ind(const struct tgsi_ind_register *reg) +{ + struct tgsi_src_register src = { 0 }; + + src.File = reg->File; + src.Index = reg->Index; + src.SwizzleX = reg->Swizzle; + src.SwizzleY = reg->Swizzle; + src.SwizzleZ = reg->Swizzle; + src.SwizzleW = reg->Swizzle; + + return src; +} + +/** + * Return the dimension of the texture coordinates (layer included for array + * textures), as well as the location of the shadow reference value or the + * sample index. + */ +int +tgsi_util_get_texture_coord_dim(int tgsi_tex, int *shadow_or_sample) +{ + int dim; + + /* + * Depending on the texture target, (src0.xyzw, src1.x) is interpreted + * differently: + * + * (s, X, X, X, X), for BUFFER + * (s, X, X, X, X), for 1D + * (s, t, X, X, X), for 2D, RECT + * (s, t, r, X, X), for 3D, CUBE + * + * (s, layer, X, X, X), for 1D_ARRAY + * (s, t, layer, X, X), for 2D_ARRAY + * (s, t, r, layer, X), for CUBE_ARRAY + * + * (s, X, shadow, X, X), for SHADOW1D + * (s, t, shadow, X, X), for SHADOW2D, SHADOWRECT + * (s, t, r, shadow, X), for SHADOWCUBE + * + * (s, layer, shadow, X, X), for SHADOW1D_ARRAY + * (s, t, layer, shadow, X), for SHADOW2D_ARRAY + * (s, t, r, layer, shadow), for SHADOWCUBE_ARRAY + * + * (s, t, sample, X, X), for 2D_MSAA + * (s, t, layer, sample, X), for 2D_ARRAY_MSAA + */ + switch (tgsi_tex) { + case TGSI_TEXTURE_BUFFER: + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_SHADOW1D: + dim = 1; + break; + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_2D_MSAA: + dim = 2; + break; + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + dim = 3; + break; + case TGSI_TEXTURE_CUBE_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + dim = 4; + break; + default: + assert(!"unknown texture target"); + dim = 0; + break; + } + + if (shadow_or_sample) { + switch (tgsi_tex) { + case TGSI_TEXTURE_SHADOW1D: + /* there is a gap */ + *shadow_or_sample = 2; + break; + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + *shadow_or_sample = dim; + break; + case TGSI_TEXTURE_2D_MSAA: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + *shadow_or_sample = 3; + break; + default: + /* no shadow nor sample */ + *shadow_or_sample = -1; + break; + } + } + + return dim; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_util.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_util.h new file mode 100644 index 0000000..deb1ecc --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/tgsi/tgsi_util.h @@ -0,0 +1,89 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef TGSI_UTIL_H +#define TGSI_UTIL_H + +#if defined __cplusplus +extern "C" { +#endif + +struct tgsi_src_register; +struct tgsi_full_src_register; +struct tgsi_full_instruction; + +void * +tgsi_align_128bit( + void *unaligned ); + +unsigned +tgsi_util_get_src_register_swizzle( + const struct tgsi_src_register *reg, + unsigned component ); + + +unsigned +tgsi_util_get_full_src_register_swizzle( + const struct tgsi_full_src_register *reg, + unsigned component ); + +void +tgsi_util_set_src_register_swizzle( + struct tgsi_src_register *reg, + unsigned swizzle, + unsigned component ); + +#define TGSI_UTIL_SIGN_CLEAR 0 /* Force positive */ +#define TGSI_UTIL_SIGN_SET 1 /* Force negative */ +#define TGSI_UTIL_SIGN_TOGGLE 2 /* Negate */ +#define TGSI_UTIL_SIGN_KEEP 3 /* No change */ + +unsigned +tgsi_util_get_full_src_register_sign_mode( + const struct tgsi_full_src_register *reg, + unsigned component ); + +void +tgsi_util_set_full_src_register_sign_mode( + struct tgsi_full_src_register *reg, + unsigned sign_mode ); + +unsigned +tgsi_util_get_inst_usage_mask(const struct tgsi_full_instruction *inst, + unsigned src_idx); + +struct tgsi_src_register +tgsi_util_get_src_from_ind(const struct tgsi_ind_register *reg); + +int +tgsi_util_get_texture_coord_dim(int tgsi_tex, int *shadow_or_sample); + +#if defined __cplusplus +} +#endif + +#endif /* TGSI_UTIL_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/__pycache__/u_format_parse.cpython-310.pyc b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/__pycache__/u_format_parse.cpython-310.pyc new file mode 100644 index 0000000..4a6dcc9 Binary files /dev/null and b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/__pycache__/u_format_parse.cpython-310.pyc differ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/__pycache__/u_format_parse.cpython-36.pyc b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/__pycache__/u_format_parse.cpython-36.pyc new file mode 100644 index 0000000..d08e47a Binary files /dev/null and b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/__pycache__/u_format_parse.cpython-36.pyc differ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/rgtc.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/rgtc.c new file mode 100644 index 0000000..6886ac0 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/rgtc.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Red Hat Inc. + * + * block compression parts are: + * Copyright (C) 2004 Roland Scheidegger All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: + * Dave Airlie + */ + +#include +#include "macros.h" + +#include "rgtc.h" + +#define RGTC_DEBUG 0 + +#define TAG(x) util_format_unsigned_##x + +#define TYPE unsigned char +#define T_MIN 0 +#define T_MAX 0xff + +#include "texcompress_rgtc_tmp.h" + +#undef TAG +#undef TYPE +#undef T_MIN +#undef T_MAX + +#define TAG(x) util_format_signed_##x +#define TYPE signed char +#define T_MIN (signed char)-128 +#define T_MAX (signed char)127 + +#include "texcompress_rgtc_tmp.h" + +#undef TAG +#undef TYPE +#undef T_MIN +#undef T_MAX + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/rgtc.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/rgtc.h new file mode 100644 index 0000000..6bba9f2 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/rgtc.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2014 Red Hat + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + */ + +#ifndef _RGTC_H +#define _RGTC_H + +void util_format_unsigned_fetch_texel_rgtc(unsigned srcRowStride, const unsigned char *pixdata, + unsigned i, unsigned j, unsigned char *value, unsigned comps); + +void util_format_signed_fetch_texel_rgtc(unsigned srcRowStride, const signed char *pixdata, + unsigned i, unsigned j, signed char *value, unsigned comps); + +void util_format_unsigned_encode_rgtc_ubyte(unsigned char *blkaddr, unsigned char srccolors[4][4], + int numxpixels, int numypixels); + +void util_format_signed_encode_rgtc_ubyte(signed char *blkaddr, signed char srccolors[4][4], + int numxpixels, int numypixels); +#endif /* _RGTC_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_atomic.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_atomic.h new file mode 100644 index 0000000..1348456 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_atomic.h @@ -0,0 +1,349 @@ +/** + * Many similar implementations exist. See for example libwsbm + * or the linux kernel include/atomic.h + * + * No copyright claimed on this file. + * + */ + +#ifndef U_ATOMIC_H +#define U_ATOMIC_H + +#include "pipe/p_compiler.h" +#include "pipe/p_defines.h" + +/* Favor OS-provided implementations. + * + * Where no OS-provided implementation is available, fall back to + * locally coded assembly, compiler intrinsic or ultimately a + * mutex-based implementation. + */ +#if defined(PIPE_OS_SOLARIS) +#define PIPE_ATOMIC_OS_SOLARIS +#elif defined(PIPE_CC_MSVC) +#define PIPE_ATOMIC_MSVC_INTRINSIC +#elif (defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86)) +#define PIPE_ATOMIC_ASM_MSVC_X86 +#elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86)) +#define PIPE_ATOMIC_ASM_GCC_X86 +#elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86_64)) +#define PIPE_ATOMIC_ASM_GCC_X86_64 +#elif defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 401) +#define PIPE_ATOMIC_GCC_INTRINSIC +#else +#error "Unsupported platform" +#endif + + +#if defined(PIPE_ATOMIC_ASM_GCC_X86_64) +#define PIPE_ATOMIC "GCC x86_64 assembly" + +#ifdef __cplusplus +extern "C" { +#endif + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +static inline boolean +p_atomic_dec_zero(int32_t *v) +{ + unsigned char c; + + __asm__ __volatile__("lock; decl %0; sete %1":"+m"(*v), "=qm"(c) + ::"memory"); + + return c != 0; +} + +static inline void +p_atomic_inc(int32_t *v) +{ + __asm__ __volatile__("lock; incl %0":"+m"(*v)); +} + +static inline void +p_atomic_dec(int32_t *v) +{ + __asm__ __volatile__("lock; decl %0":"+m"(*v)); +} + +static inline int32_t +p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new) +{ + return __sync_val_compare_and_swap(v, old, _new); +} + +#ifdef __cplusplus +} +#endif + +#endif /* PIPE_ATOMIC_ASM_GCC_X86_64 */ + + +#if defined(PIPE_ATOMIC_ASM_GCC_X86) + +#define PIPE_ATOMIC "GCC x86 assembly" + +#ifdef __cplusplus +extern "C" { +#endif + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +static inline boolean +p_atomic_dec_zero(int32_t *v) +{ + unsigned char c; + + __asm__ __volatile__("lock; decl %0; sete %1":"+m"(*v), "=qm"(c) + ::"memory"); + + return c != 0; +} + +static inline void +p_atomic_inc(int32_t *v) +{ + __asm__ __volatile__("lock; incl %0":"+m"(*v)); +} + +static inline void +p_atomic_dec(int32_t *v) +{ + __asm__ __volatile__("lock; decl %0":"+m"(*v)); +} + +static inline int32_t +p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new) +{ + return __sync_val_compare_and_swap(v, old, _new); +} + +#ifdef __cplusplus +} +#endif + +#endif + + + +/* Implementation using GCC-provided synchronization intrinsics + */ +#if defined(PIPE_ATOMIC_GCC_INTRINSIC) + +#define PIPE_ATOMIC "GCC Sync Intrinsics" + +#ifdef __cplusplus +extern "C" { +#endif + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +static inline boolean +p_atomic_dec_zero(int32_t *v) +{ + return (__sync_sub_and_fetch(v, 1) == 0); +} + +static inline void +p_atomic_inc(int32_t *v) +{ + (void) __sync_add_and_fetch(v, 1); +} + +static inline void +p_atomic_dec(int32_t *v) +{ + (void) __sync_sub_and_fetch(v, 1); +} + +static inline int32_t +p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new) +{ + return __sync_val_compare_and_swap(v, old, _new); +} + +#ifdef __cplusplus +} +#endif + +#endif + + + +/* Unlocked version for single threaded environments, such as some + * windows kernel modules. + */ +#if defined(PIPE_ATOMIC_OS_UNLOCKED) + +#define PIPE_ATOMIC "Unlocked" + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) +#define p_atomic_dec_zero(_v) ((boolean) --(*(_v))) +#define p_atomic_inc(_v) ((void) (*(_v))++) +#define p_atomic_dec(_v) ((void) (*(_v))--) +#define p_atomic_cmpxchg(_v, old, _new) (*(_v) == old ? *(_v) = (_new) : *(_v)) + +#endif + + +/* Locally coded assembly for MSVC on x86: + */ +#if defined(PIPE_ATOMIC_ASM_MSVC_X86) + +#define PIPE_ATOMIC "MSVC x86 assembly" + +#ifdef __cplusplus +extern "C" { +#endif + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +static inline boolean +p_atomic_dec_zero(int32_t *v) +{ + unsigned char c; + + __asm { + mov eax, [v] + lock dec dword ptr [eax] + sete byte ptr [c] + } + + return c != 0; +} + +static inline void +p_atomic_inc(int32_t *v) +{ + __asm { + mov eax, [v] + lock inc dword ptr [eax] + } +} + +static inline void +p_atomic_dec(int32_t *v) +{ + __asm { + mov eax, [v] + lock dec dword ptr [eax] + } +} + +static inline int32_t +p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new) +{ + int32_t orig; + + __asm { + mov ecx, [v] + mov eax, [old] + mov edx, [_new] + lock cmpxchg [ecx], edx + mov [orig], eax + } + + return orig; +} + +#ifdef __cplusplus +} +#endif + +#endif + + +#if defined(PIPE_ATOMIC_MSVC_INTRINSIC) + +#define PIPE_ATOMIC "MSVC Intrinsics" + +#include + +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedCompareExchange) + +#ifdef __cplusplus +extern "C" { +#endif + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +static inline boolean +p_atomic_dec_zero(int32_t *v) +{ + return _InterlockedDecrement((long *)v) == 0; +} + +static inline void +p_atomic_inc(int32_t *v) +{ + _InterlockedIncrement((long *)v); +} + +static inline void +p_atomic_dec(int32_t *v) +{ + _InterlockedDecrement((long *)v); +} + +static inline int32_t +p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new) +{ + return _InterlockedCompareExchange((long *)v, _new, old); +} + +#ifdef __cplusplus +} +#endif + +#endif + +#if defined(PIPE_ATOMIC_OS_SOLARIS) + +#define PIPE_ATOMIC "Solaris OS atomic functions" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +static inline boolean +p_atomic_dec_zero(int32_t *v) +{ + uint32_t n = atomic_dec_32_nv((uint32_t *) v); + + return n != 0; +} + +#define p_atomic_inc(_v) atomic_inc_32((uint32_t *) _v) +#define p_atomic_dec(_v) atomic_dec_32((uint32_t *) _v) + +#define p_atomic_cmpxchg(_v, _old, _new) \ + atomic_cas_32( (uint32_t *) _v, (uint32_t) _old, (uint32_t) _new) + +#ifdef __cplusplus +} +#endif + +#endif + + +#ifndef PIPE_ATOMIC +#error "No pipe_atomic implementation selected" +#endif + + + +#endif /* U_ATOMIC_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_bitmask.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_bitmask.c new file mode 100644 index 0000000..b19be29 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_bitmask.c @@ -0,0 +1,328 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Generic bitmask implementation. + * + * @author Jose Fonseca + */ + + +#include "pipe/p_compiler.h" +#include "util/u_debug.h" + +#include "util/u_memory.h" +#include "util/u_bitmask.h" + + +typedef uint32_t util_bitmask_word; + + +#define UTIL_BITMASK_INITIAL_WORDS 16 +#define UTIL_BITMASK_BITS_PER_BYTE 8 +#define UTIL_BITMASK_BITS_PER_WORD (sizeof(util_bitmask_word) * UTIL_BITMASK_BITS_PER_BYTE) + + +struct util_bitmask +{ + util_bitmask_word *words; + + /** Number of bits we can currently hold */ + unsigned size; + + /** Number of consecutive bits set at the start of the bitmask */ + unsigned filled; +}; + + +struct util_bitmask * +util_bitmask_create(void) +{ + struct util_bitmask *bm; + + bm = MALLOC_STRUCT(util_bitmask); + if(!bm) + return NULL; + + bm->words = (util_bitmask_word *)CALLOC(UTIL_BITMASK_INITIAL_WORDS, sizeof(util_bitmask_word)); + if(!bm->words) { + FREE(bm); + return NULL; + } + + bm->size = UTIL_BITMASK_INITIAL_WORDS * UTIL_BITMASK_BITS_PER_WORD; + bm->filled = 0; + + return bm; +} + + +/** + * Resize the bitmask if necessary + */ +static inline boolean +util_bitmask_resize(struct util_bitmask *bm, + unsigned minimum_index) +{ + unsigned minimum_size = minimum_index + 1; + unsigned new_size; + util_bitmask_word *new_words; + + /* Check integer overflow */ + if(!minimum_size) + return FALSE; + + if(bm->size >= minimum_size) + return TRUE; + + assert(bm->size % UTIL_BITMASK_BITS_PER_WORD == 0); + new_size = bm->size; + while(new_size < minimum_size) { + new_size *= 2; + /* Check integer overflow */ + if(new_size < bm->size) + return FALSE; + } + assert(new_size); + assert(new_size % UTIL_BITMASK_BITS_PER_WORD == 0); + + new_words = (util_bitmask_word *)REALLOC((void *)bm->words, + bm->size / UTIL_BITMASK_BITS_PER_BYTE, + new_size / UTIL_BITMASK_BITS_PER_BYTE); + if(!new_words) + return FALSE; + + memset(new_words + bm->size/UTIL_BITMASK_BITS_PER_WORD, + 0, + (new_size - bm->size)/UTIL_BITMASK_BITS_PER_BYTE); + + bm->size = new_size; + bm->words = new_words; + + return TRUE; +} + + +/** + * Lazily update the filled. + */ +static inline void +util_bitmask_filled_set(struct util_bitmask *bm, + unsigned index) +{ + assert(bm->filled <= bm->size); + assert(index < bm->size); + + if(index == bm->filled) { + ++bm->filled; + assert(bm->filled <= bm->size); + } +} + +static inline void +util_bitmask_filled_unset(struct util_bitmask *bm, + unsigned index) +{ + assert(bm->filled <= bm->size); + assert(index < bm->size); + + if(index < bm->filled) + bm->filled = index; +} + + +unsigned +util_bitmask_add(struct util_bitmask *bm) +{ + unsigned word; + unsigned bit; + util_bitmask_word mask; + + assert(bm); + + /* linear search for an empty index */ + word = bm->filled / UTIL_BITMASK_BITS_PER_WORD; + bit = bm->filled % UTIL_BITMASK_BITS_PER_WORD; + mask = 1 << bit; + while(word < bm->size / UTIL_BITMASK_BITS_PER_WORD) { + while(bit < UTIL_BITMASK_BITS_PER_WORD) { + if(!(bm->words[word] & mask)) + goto found; + ++bm->filled; + ++bit; + mask <<= 1; + } + ++word; + bit = 0; + mask = 1; + } +found: + + /* grow the bitmask if necessary */ + if(!util_bitmask_resize(bm, bm->filled)) + return UTIL_BITMASK_INVALID_INDEX; + + assert(!(bm->words[word] & mask)); + bm->words[word] |= mask; + + return bm->filled++; +} + + +unsigned +util_bitmask_set(struct util_bitmask *bm, + unsigned index) +{ + unsigned word; + unsigned bit; + util_bitmask_word mask; + + assert(bm); + + /* grow the bitmask if necessary */ + if(!util_bitmask_resize(bm, index)) + return UTIL_BITMASK_INVALID_INDEX; + + word = index / UTIL_BITMASK_BITS_PER_WORD; + bit = index % UTIL_BITMASK_BITS_PER_WORD; + mask = 1 << bit; + + bm->words[word] |= mask; + + util_bitmask_filled_set(bm, index); + + return index; +} + + +void +util_bitmask_clear(struct util_bitmask *bm, + unsigned index) +{ + unsigned word; + unsigned bit; + util_bitmask_word mask; + + assert(bm); + + if(index >= bm->size) + return; + + word = index / UTIL_BITMASK_BITS_PER_WORD; + bit = index % UTIL_BITMASK_BITS_PER_WORD; + mask = 1 << bit; + + bm->words[word] &= ~mask; + + util_bitmask_filled_unset(bm, index); +} + + +boolean +util_bitmask_get(struct util_bitmask *bm, + unsigned index) +{ + unsigned word = index / UTIL_BITMASK_BITS_PER_WORD; + unsigned bit = index % UTIL_BITMASK_BITS_PER_WORD; + util_bitmask_word mask = 1 << bit; + + assert(bm); + + if(index < bm->filled) { + assert(bm->words[word] & mask); + return TRUE; + } + + if(index >= bm->size) + return FALSE; + + if(bm->words[word] & mask) { + util_bitmask_filled_set(bm, index); + return TRUE; + } + else + return FALSE; +} + + +unsigned +util_bitmask_get_next_index(struct util_bitmask *bm, + unsigned index) +{ + unsigned word = index / UTIL_BITMASK_BITS_PER_WORD; + unsigned bit = index % UTIL_BITMASK_BITS_PER_WORD; + util_bitmask_word mask = 1 << bit; + + if(index < bm->filled) { + assert(bm->words[word] & mask); + return index; + } + + if(index >= bm->size) { + return UTIL_BITMASK_INVALID_INDEX; + } + + /* Do a linear search */ + while(word < bm->size / UTIL_BITMASK_BITS_PER_WORD) { + while(bit < UTIL_BITMASK_BITS_PER_WORD) { + if(bm->words[word] & mask) { + if(index == bm->filled) { + ++bm->filled; + assert(bm->filled <= bm->size); + } + return index; + } + ++index; + ++bit; + mask <<= 1; + } + ++word; + bit = 0; + mask = 1; + } + + return UTIL_BITMASK_INVALID_INDEX; +} + + +unsigned +util_bitmask_get_first_index(struct util_bitmask *bm) +{ + return util_bitmask_get_next_index(bm, 0); +} + + +void +util_bitmask_destroy(struct util_bitmask *bm) +{ + assert(bm); + + FREE(bm->words); + FREE(bm); +} + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_bitmask.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_bitmask.h new file mode 100644 index 0000000..98b85dd --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_bitmask.h @@ -0,0 +1,117 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Generic bitmask. + * + * @author Jose Fonseca + */ + +#ifndef U_HANDLE_BITMASK_H_ +#define U_HANDLE_BITMASK_H_ + + +#include "pipe/p_compiler.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define UTIL_BITMASK_INVALID_INDEX (~0U) + + +/** + * Abstract data type to represent arbitrary set of bits. + */ +struct util_bitmask; + + +struct util_bitmask * +util_bitmask_create(void); + + +/** + * Search a cleared bit and set it. + * + * It searches for the first cleared bit. + * + * Returns the bit index on success, or UTIL_BITMASK_INVALID_INDEX on out of + * memory growing the bitmask. + */ +unsigned +util_bitmask_add(struct util_bitmask *bm); + +/** + * Set a bit. + * + * Returns the input index on success, or UTIL_BITMASK_INVALID_INDEX on out of + * memory growing the bitmask. + */ +unsigned +util_bitmask_set(struct util_bitmask *bm, + unsigned index); + +void +util_bitmask_clear(struct util_bitmask *bm, + unsigned index); + +boolean +util_bitmask_get(struct util_bitmask *bm, + unsigned index); + + +void +util_bitmask_destroy(struct util_bitmask *bm); + + +/** + * Search for the first set bit. + * + * Returns UTIL_BITMASK_INVALID_INDEX if a set bit cannot be found. + */ +unsigned +util_bitmask_get_first_index(struct util_bitmask *bm); + + +/** + * Search for the first set bit, starting from the giving index. + * + * Returns UTIL_BITMASK_INVALID_INDEX if a set bit cannot be found. + */ +unsigned +util_bitmask_get_next_index(struct util_bitmask *bm, + unsigned index); + + +#ifdef __cplusplus +} +#endif + +#endif /* U_HANDLE_BITMASK_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_box.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_box.h new file mode 100644 index 0000000..2fa6017 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_box.h @@ -0,0 +1,80 @@ +#ifndef UTIL_BOX_INLINES_H +#define UTIL_BOX_INLINES_H + +#include "pipe/p_state.h" + +static inline +void u_box_1d( unsigned x, + unsigned w, + struct pipe_box *box ) +{ + box->x = x; + box->y = 0; + box->z = 0; + box->width = w; + box->height = 1; + box->depth = 1; +} + +static inline +void u_box_2d( unsigned x, + unsigned y, + unsigned w, + unsigned h, + struct pipe_box *box ) +{ + box->x = x; + box->y = y; + box->z = 0; + box->width = w; + box->height = h; + box->depth = 1; +} + +static inline +void u_box_origin_2d( unsigned w, + unsigned h, + struct pipe_box *box ) +{ + box->x = 0; + box->y = 0; + box->z = 0; + box->width = w; + box->height = h; + box->depth = 1; +} + +static inline +void u_box_2d_zslice( unsigned x, + unsigned y, + unsigned z, + unsigned w, + unsigned h, + struct pipe_box *box ) +{ + box->x = x; + box->y = y; + box->z = z; + box->width = w; + box->height = h; + box->depth = 1; +} + +static inline +void u_box_3d( unsigned x, + unsigned y, + unsigned z, + unsigned w, + unsigned h, + unsigned d, + struct pipe_box *box ) +{ + box->x = x; + box->y = y; + box->z = z; + box->width = w; + box->height = h; + box->depth = d; +} + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_cpu_detect.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_cpu_detect.c new file mode 100644 index 0000000..0b4b83a --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_cpu_detect.c @@ -0,0 +1,458 @@ +/************************************************************************** + * + * Copyright 2008 Dennis Smit + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * CPU feature detection. + * + * @author Dennis Smit + * @author Based on the work of Eric Anholt + */ + +#include "pipe/p_config.h" + +#include "u_debug.h" +#include "u_cpu_detect.h" + +#if defined(PIPE_ARCH_PPC) +#if defined(PIPE_OS_APPLE) +#include +#else +#include +#include +#endif +#endif + +#if defined(PIPE_OS_NETBSD) || defined(PIPE_OS_OPENBSD) +#include +#include +#include +#endif + +#if defined(PIPE_OS_FREEBSD) +#include +#include +#endif + +#if defined(PIPE_OS_LINUX) +#include +#endif + +#ifdef PIPE_OS_UNIX +#include +#endif + +#if defined(PIPE_OS_WINDOWS) +#include +#if defined(PIPE_CC_MSVC) +#include +#endif +#endif + + +#ifdef DEBUG +DEBUG_GET_ONCE_BOOL_OPTION(dump_cpu, "GALLIUM_DUMP_CPU", FALSE) +#endif + + +struct util_cpu_caps util_cpu_caps; + +#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) +static int has_cpuid(void); +#endif + + +#if defined(PIPE_ARCH_PPC) && !defined(PIPE_OS_APPLE) +static jmp_buf __lv_powerpc_jmpbuf; +static volatile sig_atomic_t __lv_powerpc_canjump = 0; + +static void +sigill_handler(int sig) +{ + if (!__lv_powerpc_canjump) { + signal (sig, SIG_DFL); + raise (sig); + } + + __lv_powerpc_canjump = 0; + longjmp(__lv_powerpc_jmpbuf, 1); +} +#endif + +#if defined(PIPE_ARCH_PPC) +static void +check_os_altivec_support(void) +{ +#if defined(PIPE_OS_APPLE) + int sels[2] = {CTL_HW, HW_VECTORUNIT}; + int has_vu = 0; + int len = sizeof (has_vu); + int err; + + err = sysctl(sels, 2, &has_vu, &len, NULL, 0); + + if (err == 0) { + if (has_vu != 0) { + util_cpu_caps.has_altivec = 1; + } + } +#else /* !PIPE_OS_APPLE */ + /* not on Apple/Darwin, do it the brute-force way */ + /* this is borrowed from the libmpeg2 library */ + signal(SIGILL, sigill_handler); + if (setjmp(__lv_powerpc_jmpbuf)) { + signal(SIGILL, SIG_DFL); + } else { + __lv_powerpc_canjump = 1; + + __asm __volatile + ("mtspr 256, %0\n\t" + "vand %%v0, %%v0, %%v0" + : + : "r" (-1)); + + signal(SIGILL, SIG_DFL); + util_cpu_caps.has_altivec = 1; + } +#endif /* !PIPE_OS_APPLE */ +} +#endif /* PIPE_ARCH_PPC */ + + +#if defined(PIPE_ARCH_X86) || defined (PIPE_ARCH_X86_64) +static int has_cpuid(void) +{ +#if defined(PIPE_ARCH_X86) +#if defined(PIPE_OS_GCC) + int a, c; + + __asm __volatile + ("pushf\n" + "popl %0\n" + "movl %0, %1\n" + "xorl $0x200000, %0\n" + "push %0\n" + "popf\n" + "pushf\n" + "popl %0\n" + : "=a" (a), "=c" (c) + : + : "cc"); + + return a != c; +#else + /* FIXME */ + return 1; +#endif +#elif defined(PIPE_ARCH_X86_64) + return 1; +#else + return 0; +#endif +} + + +/** + * @sa cpuid.h included in gcc-4.3 onwards. + * @sa http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + */ +static inline void +cpuid(uint32_t ax, uint32_t *p) +{ +#if (defined(PIPE_CC_GCC) || defined(PIPE_CC_SUNPRO)) && defined(PIPE_ARCH_X86) + __asm __volatile ( + "xchgl %%ebx, %1\n\t" + "cpuid\n\t" + "xchgl %%ebx, %1" + : "=a" (p[0]), + "=S" (p[1]), + "=c" (p[2]), + "=d" (p[3]) + : "0" (ax) + ); +#elif (defined(PIPE_CC_GCC) || defined(PIPE_CC_SUNPRO)) && defined(PIPE_ARCH_X86_64) + __asm __volatile ( + "cpuid\n\t" + : "=a" (p[0]), + "=b" (p[1]), + "=c" (p[2]), + "=d" (p[3]) + : "0" (ax) + ); +#elif defined(PIPE_CC_MSVC) + __cpuid(p, ax); +#else + p[0] = 0; + p[1] = 0; + p[2] = 0; + p[3] = 0; +#endif +} + +/** + * @sa cpuid.h included in gcc-4.4 onwards. + * @sa http://msdn.microsoft.com/en-us/library/hskdteyh%28v=vs.90%29.aspx + */ +static inline void +cpuid_count(uint32_t ax, uint32_t cx, uint32_t *p) +{ +#if (defined(PIPE_CC_GCC) || defined(PIPE_CC_SUNPRO)) && defined(PIPE_ARCH_X86) + __asm __volatile ( + "xchgl %%ebx, %1\n\t" + "cpuid\n\t" + "xchgl %%ebx, %1" + : "=a" (p[0]), + "=S" (p[1]), + "=c" (p[2]), + "=d" (p[3]) + : "0" (ax), "2" (cx) + ); +#elif (defined(PIPE_CC_GCC) || defined(PIPE_CC_SUNPRO)) && defined(PIPE_ARCH_X86_64) + __asm __volatile ( + "cpuid\n\t" + : "=a" (p[0]), + "=b" (p[1]), + "=c" (p[2]), + "=d" (p[3]) + : "0" (ax), "2" (cx) + ); +#elif defined(PIPE_CC_MSVC) + __cpuidex(p, ax, cx); +#else + p[0] = 0; + p[1] = 0; + p[2] = 0; + p[3] = 0; +#endif +} + + +static inline uint64_t xgetbv(void) +{ +#if defined(PIPE_CC_GCC) + uint32_t eax, edx; + + __asm __volatile ( + ".byte 0x0f, 0x01, 0xd0" // xgetbv isn't supported on gcc < 4.4 + : "=a"(eax), + "=d"(edx) + : "c"(0) + ); + + return ((uint64_t)edx << 32) | eax; +#elif defined(PIPE_CC_MSVC) && defined(_MSC_FULL_VER) && defined(_XCR_XFEATURE_ENABLED_MASK) + return _xgetbv(_XCR_XFEATURE_ENABLED_MASK); +#else + return 0; +#endif +} + + +#if defined(PIPE_ARCH_X86) +static inline boolean sse2_has_daz(void) +{ + struct { + uint32_t pad1[7]; + uint32_t mxcsr_mask; + uint32_t pad2[128-8]; + } PIPE_ALIGN_VAR(16) fxarea; + + fxarea.mxcsr_mask = 0; +#if (defined(PIPE_CC_GCC) || defined(PIPE_CC_SUNPRO)) + __asm __volatile ("fxsave %0" : "+m" (fxarea)); +#elif (defined(PIPE_CC_MSVC) && _MSC_VER >= 1700) || defined(PIPE_CC_ICL) + /* 1700 = Visual Studio 2012 */ + _fxsave(&fxarea); +#else + fxarea.mxcsr_mask = 0; +#endif + return !!(fxarea.mxcsr_mask & (1 << 6)); +} +#endif + +#endif /* X86 or X86_64 */ + +void +util_cpu_detect(void) +{ + static boolean util_cpu_detect_initialized = FALSE; + + if(util_cpu_detect_initialized) + return; + + memset(&util_cpu_caps, 0, sizeof util_cpu_caps); + + /* Count the number of CPUs in system */ +#if defined(PIPE_OS_WINDOWS) + { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + util_cpu_caps.nr_cpus = system_info.dwNumberOfProcessors; + } +#elif defined(PIPE_OS_UNIX) && defined(_SC_NPROCESSORS_ONLN) + util_cpu_caps.nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if (util_cpu_caps.nr_cpus == -1) + util_cpu_caps.nr_cpus = 1; +#elif defined(PIPE_OS_BSD) + { + int mib[2], ncpu; + int len; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + + len = sizeof (ncpu); + sysctl(mib, 2, &ncpu, &len, NULL, 0); + util_cpu_caps.nr_cpus = ncpu; + } +#else + util_cpu_caps.nr_cpus = 1; +#endif + + /* Make the fallback cacheline size nonzero so that it can be + * safely passed to align(). + */ + util_cpu_caps.cacheline = sizeof(void *); + +#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) + if (has_cpuid()) { + uint32_t regs[4]; + uint32_t regs2[4]; + + util_cpu_caps.cacheline = 32; + + /* Get max cpuid level */ + cpuid(0x00000000, regs); + + if (regs[0] >= 0x00000001) { + unsigned int cacheline; + + cpuid (0x00000001, regs2); + + util_cpu_caps.x86_cpu_type = (regs2[0] >> 8) & 0xf; + if (util_cpu_caps.x86_cpu_type == 0xf) + util_cpu_caps.x86_cpu_type = 8 + ((regs2[0] >> 20) & 255); /* use extended family (P4, IA64) */ + + /* general feature flags */ + util_cpu_caps.has_tsc = (regs2[3] >> 4) & 1; /* 0x0000010 */ + util_cpu_caps.has_mmx = (regs2[3] >> 23) & 1; /* 0x0800000 */ + util_cpu_caps.has_sse = (regs2[3] >> 25) & 1; /* 0x2000000 */ + util_cpu_caps.has_sse2 = (regs2[3] >> 26) & 1; /* 0x4000000 */ + util_cpu_caps.has_sse3 = (regs2[2] >> 0) & 1; /* 0x0000001 */ + util_cpu_caps.has_ssse3 = (regs2[2] >> 9) & 1; /* 0x0000020 */ + util_cpu_caps.has_sse4_1 = (regs2[2] >> 19) & 1; + util_cpu_caps.has_sse4_2 = (regs2[2] >> 20) & 1; + util_cpu_caps.has_popcnt = (regs2[2] >> 23) & 1; + util_cpu_caps.has_avx = ((regs2[2] >> 28) & 1) && // AVX + ((regs2[2] >> 27) & 1) && // OSXSAVE + ((xgetbv() & 6) == 6); // XMM & YMM + util_cpu_caps.has_f16c = (regs2[2] >> 29) & 1; + util_cpu_caps.has_mmx2 = util_cpu_caps.has_sse; /* SSE cpus supports mmxext too */ +#if defined(PIPE_ARCH_X86_64) + util_cpu_caps.has_daz = 1; +#else + util_cpu_caps.has_daz = util_cpu_caps.has_sse3 || + (util_cpu_caps.has_sse2 && sse2_has_daz()); +#endif + + cacheline = ((regs2[1] >> 8) & 0xFF) * 8; + if (cacheline > 0) + util_cpu_caps.cacheline = cacheline; + } + if (util_cpu_caps.has_avx && regs[0] >= 0x00000007) { + uint32_t regs7[4]; + cpuid_count(0x00000007, 0x00000000, regs7); + util_cpu_caps.has_avx2 = (regs7[1] >> 5) & 1; + } + + if (regs[1] == 0x756e6547 && regs[2] == 0x6c65746e && regs[3] == 0x49656e69) { + /* GenuineIntel */ + util_cpu_caps.has_intel = 1; + } + + cpuid(0x80000000, regs); + + if (regs[0] >= 0x80000001) { + + cpuid(0x80000001, regs2); + + util_cpu_caps.has_mmx |= (regs2[3] >> 23) & 1; + util_cpu_caps.has_mmx2 |= (regs2[3] >> 22) & 1; + util_cpu_caps.has_3dnow = (regs2[3] >> 31) & 1; + util_cpu_caps.has_3dnow_ext = (regs2[3] >> 30) & 1; + + util_cpu_caps.has_xop = util_cpu_caps.has_avx && + ((regs2[2] >> 11) & 1); + } + + if (regs[0] >= 0x80000006) { + cpuid(0x80000006, regs2); + util_cpu_caps.cacheline = regs2[2] & 0xFF; + } + + if (!util_cpu_caps.has_sse) { + util_cpu_caps.has_sse2 = 0; + util_cpu_caps.has_sse3 = 0; + util_cpu_caps.has_ssse3 = 0; + util_cpu_caps.has_sse4_1 = 0; + } + } +#endif /* PIPE_ARCH_X86 || PIPE_ARCH_X86_64 */ + +#if defined(PIPE_ARCH_PPC) + check_os_altivec_support(); +#endif /* PIPE_ARCH_PPC */ + +#ifdef DEBUG + if (debug_get_option_dump_cpu()) { + debug_printf("util_cpu_caps.nr_cpus = %u\n", util_cpu_caps.nr_cpus); + + debug_printf("util_cpu_caps.x86_cpu_type = %u\n", util_cpu_caps.x86_cpu_type); + debug_printf("util_cpu_caps.cacheline = %u\n", util_cpu_caps.cacheline); + + debug_printf("util_cpu_caps.has_tsc = %u\n", util_cpu_caps.has_tsc); + debug_printf("util_cpu_caps.has_mmx = %u\n", util_cpu_caps.has_mmx); + debug_printf("util_cpu_caps.has_mmx2 = %u\n", util_cpu_caps.has_mmx2); + debug_printf("util_cpu_caps.has_sse = %u\n", util_cpu_caps.has_sse); + debug_printf("util_cpu_caps.has_sse2 = %u\n", util_cpu_caps.has_sse2); + debug_printf("util_cpu_caps.has_sse3 = %u\n", util_cpu_caps.has_sse3); + debug_printf("util_cpu_caps.has_ssse3 = %u\n", util_cpu_caps.has_ssse3); + debug_printf("util_cpu_caps.has_sse4_1 = %u\n", util_cpu_caps.has_sse4_1); + debug_printf("util_cpu_caps.has_sse4_2 = %u\n", util_cpu_caps.has_sse4_2); + debug_printf("util_cpu_caps.has_avx = %u\n", util_cpu_caps.has_avx); + debug_printf("util_cpu_caps.has_avx2 = %u\n", util_cpu_caps.has_avx2); + debug_printf("util_cpu_caps.has_f16c = %u\n", util_cpu_caps.has_f16c); + debug_printf("util_cpu_caps.has_popcnt = %u\n", util_cpu_caps.has_popcnt); + debug_printf("util_cpu_caps.has_3dnow = %u\n", util_cpu_caps.has_3dnow); + debug_printf("util_cpu_caps.has_3dnow_ext = %u\n", util_cpu_caps.has_3dnow_ext); + debug_printf("util_cpu_caps.has_xop = %u\n", util_cpu_caps.has_xop); + debug_printf("util_cpu_caps.has_altivec = %u\n", util_cpu_caps.has_altivec); + debug_printf("util_cpu_caps.has_daz = %u\n", util_cpu_caps.has_daz); + } +#endif + + util_cpu_detect_initialized = TRUE; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_cpu_detect.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_cpu_detect.h new file mode 100644 index 0000000..01f3896 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_cpu_detect.h @@ -0,0 +1,87 @@ +/************************************************************************** + * + * Copyright 2008 Dennis Smit + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * AUTHORS, COPYRIGHT HOLDERS, AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ***************************************************************************/ + +/** + * @file + * CPU feature detection. + * + * @author Dennis Smit + * @author Based on the work of Eric Anholt + */ + +#ifndef _UTIL_CPU_DETECT_H +#define _UTIL_CPU_DETECT_H + + +#include "pipe/p_compiler.h" +#include "pipe/p_config.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +struct util_cpu_caps { + int nr_cpus; + + /* Feature flags */ + int x86_cpu_type; + unsigned cacheline; + + unsigned has_intel:1; + unsigned has_tsc:1; + unsigned has_mmx:1; + unsigned has_mmx2:1; + unsigned has_sse:1; + unsigned has_sse2:1; + unsigned has_sse3:1; + unsigned has_ssse3:1; + unsigned has_sse4_1:1; + unsigned has_sse4_2:1; + unsigned has_popcnt:1; + unsigned has_avx:1; + unsigned has_avx2:1; + unsigned has_f16c:1; + unsigned has_3dnow:1; + unsigned has_3dnow_ext:1; + unsigned has_xop:1; + unsigned has_altivec:1; + unsigned has_daz:1; +}; + +extern struct util_cpu_caps +util_cpu_caps; + +void util_cpu_detect(void); + + +#ifdef __cplusplus +} +#endif + + +#endif /* _UTIL_CPU_DETECT_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug.c new file mode 100644 index 0000000..f861168 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug.c @@ -0,0 +1,516 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * Copyright (c) 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#include "pipe/p_config.h" + +#include "pipe/p_compiler.h" +#include "util/u_debug.h" +#include "pipe/p_format.h" +#include "pipe/p_state.h" +#include "util/u_inlines.h" +#include "util/u_format.h" +#include "util/u_memory.h" +#include "util/u_string.h" +#include "util/u_math.h" +#include "util/u_prim.h" +#include "util/u_surface.h" + +#include +#include /* CHAR_BIT */ +#include /* isalnum */ + +#ifdef _WIN32 +#include +#include +#endif + + +void _debug_vprintf(const char *format, va_list ap) +{ + static char buf[4096] = {'\0'}; +#if defined(PIPE_OS_WINDOWS) || defined(PIPE_SUBSYSTEM_EMBEDDED) + /* We buffer until we find a newline. */ + size_t len = strlen(buf); + int ret = util_vsnprintf(buf + len, sizeof(buf) - len, format, ap); + if(ret > (int)(sizeof(buf) - len - 1) || util_strchr(buf + len, '\n')) { + os_log_message(buf); + buf[0] = '\0'; + } +#else + util_vsnprintf(buf, sizeof(buf), format, ap); + os_log_message(buf); +#endif +} + + +void +debug_disable_error_message_boxes(void) +{ +#ifdef _WIN32 + /* When Windows' error message boxes are disabled for this process (as is + * typically the case when running tests in an automated fashion) we disable + * CRT message boxes too. + */ + UINT uMode = SetErrorMode(0); + SetErrorMode(uMode); + if (uMode & SEM_FAILCRITICALERRORS) { + /* Disable assertion failure message box. + * http://msdn.microsoft.com/en-us/library/sas1dkb2.aspx + */ + _set_error_mode(_OUT_TO_STDERR); +#ifdef _MSC_VER + /* Disable abort message box. + * http://msdn.microsoft.com/en-us/library/e631wekh.aspx + */ + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); +#endif + } +#endif /* _WIN32 */ +} + + +#ifdef DEBUG +void debug_print_blob( const char *name, + const void *blob, + unsigned size ) +{ + const unsigned *ublob = (const unsigned *)blob; + unsigned i; + + debug_printf("%s (%d dwords%s)\n", name, size/4, + size%4 ? "... plus a few bytes" : ""); + + for (i = 0; i < size/4; i++) { + debug_printf("%d:\t%08x\n", i, ublob[i]); + } +} +#endif + + +static boolean +debug_get_option_should_print(void) +{ + static boolean first = TRUE; + static boolean value = FALSE; + + if (!first) + return value; + + /* Oh hey this will call into this function, + * but its cool since we set first to false + */ + first = FALSE; + value = debug_get_bool_option("GALLIUM_PRINT_OPTIONS", FALSE); + /* XXX should we print this option? Currently it wont */ + return value; +} + +const char * +debug_get_option(const char *name, const char *dfault) +{ + const char *result; + + result = os_get_option(name); + if(!result) + result = dfault; + + if (debug_get_option_should_print()) + debug_printf("%s: %s = %s\n", __FUNCTION__, name, result ? result : "(null)"); + + return result; +} + +boolean +debug_get_bool_option(const char *name, boolean dfault) +{ + const char *str = os_get_option(name); + boolean result; + + if(str == NULL) + result = dfault; + else if(!util_strcmp(str, "n")) + result = FALSE; + else if(!util_strcmp(str, "no")) + result = FALSE; + else if(!util_strcmp(str, "0")) + result = FALSE; + else if(!util_strcmp(str, "f")) + result = FALSE; + else if(!util_strcmp(str, "F")) + result = FALSE; + else if(!util_strcmp(str, "false")) + result = FALSE; + else if(!util_strcmp(str, "FALSE")) + result = FALSE; + else + result = TRUE; + + if (debug_get_option_should_print()) + debug_printf("%s: %s = %s\n", __FUNCTION__, name, result ? "TRUE" : "FALSE"); + + return result; +} + + +long +debug_get_num_option(const char *name, long dfault) +{ + long result; + const char *str; + + str = os_get_option(name); + if(!str) + result = dfault; + else { + long sign; + char c; + c = *str++; + if(c == '-') { + sign = -1; + c = *str++; + } + else { + sign = 1; + } + result = 0; + while('0' <= c && c <= '9') { + result = result*10 + (c - '0'); + c = *str++; + } + result *= sign; + } + + if (debug_get_option_should_print()) + debug_printf("%s: %s = %li\n", __FUNCTION__, name, result); + + return result; +} + +static boolean str_has_option(const char *str, const char *name) +{ + /* Empty string. */ + if (!*str) { + return FALSE; + } + + /* OPTION=all */ + if (!util_strcmp(str, "all")) { + return TRUE; + } + + /* Find 'name' in 'str' surrounded by non-alphanumeric characters. */ + { + const char *start = str; + unsigned name_len = strlen(name); + + /* 'start' is the beginning of the currently-parsed word, + * we increment 'str' each iteration. + * if we find either the end of string or a non-alphanumeric character, + * we compare 'start' up to 'str-1' with 'name'. */ + + while (1) { + if (!*str || !(isalnum(*str) || *str == '_')) { + if (str-start == name_len && + !memcmp(start, name, name_len)) { + return TRUE; + } + + if (!*str) { + return FALSE; + } + + start = str+1; + } + + str++; + } + } + + return FALSE; +} + +unsigned long +debug_get_flags_option(const char *name, + const struct debug_named_value *flags, + unsigned long dfault) +{ + unsigned long result; + const char *str; + const struct debug_named_value *orig = flags; + unsigned namealign = 0; + + str = os_get_option(name); + if(!str) + result = dfault; + else if (!util_strcmp(str, "help")) { + result = dfault; + _debug_printf("%s: help for %s:\n", __FUNCTION__, name); + for (; flags->name; ++flags) + namealign = MAX2(namealign, strlen(flags->name)); + for (flags = orig; flags->name; ++flags) + _debug_printf("| %*s [0x%0*lx]%s%s\n", namealign, flags->name, + (int)sizeof(unsigned long)*CHAR_BIT/4, flags->value, + flags->desc ? " " : "", flags->desc ? flags->desc : ""); + } + else { + result = 0; + while( flags->name ) { + if (str_has_option(str, flags->name)) + result |= flags->value; + ++flags; + } + } + + if (debug_get_option_should_print()) { + if (str) { + debug_printf("%s: %s = 0x%lx (%s)\n", __FUNCTION__, name, result, str); + } else { + debug_printf("%s: %s = 0x%lx\n", __FUNCTION__, name, result); + } + } + + return result; +} + + +void _debug_assert_fail(const char *expr, + const char *file, + unsigned line, + const char *function) +{ + _debug_printf("%s:%u:%s: Assertion `%s' failed.\n", file, line, function, expr); + os_abort(); +} + + +const char * +debug_dump_enum(const struct debug_named_value *names, + unsigned long value) +{ + static char rest[64]; + + while(names->name) { + if(names->value == value) + return names->name; + ++names; + } + + util_snprintf(rest, sizeof(rest), "0x%08lx", value); + return rest; +} + + +const char * +debug_dump_enum_noprefix(const struct debug_named_value *names, + const char *prefix, + unsigned long value) +{ + static char rest[64]; + + while(names->name) { + if(names->value == value) { + const char *name = names->name; + while (*name == *prefix) { + name++; + prefix++; + } + return name; + } + ++names; + } + + + + util_snprintf(rest, sizeof(rest), "0x%08lx", value); + return rest; +} + + +const char * +debug_dump_flags(const struct debug_named_value *names, + unsigned long value) +{ + static char output[4096]; + static char rest[256]; + int first = 1; + + output[0] = '\0'; + + while(names->name) { + if((names->value & value) == names->value) { + if (!first) + util_strncat(output, "|", sizeof(output) - strlen(output) - 1); + else + first = 0; + util_strncat(output, names->name, sizeof(output) - strlen(output) - 1); + output[sizeof(output) - 1] = '\0'; + value &= ~names->value; + } + ++names; + } + + if (value) { + if (!first) + util_strncat(output, "|", sizeof(output) - strlen(output) - 1); + else + first = 0; + + util_snprintf(rest, sizeof(rest), "0x%08lx", value); + util_strncat(output, rest, sizeof(output) - strlen(output) - 1); + output[sizeof(output) - 1] = '\0'; + } + + if(first) + return "0"; + + return output; +} + + +#ifdef DEBUG +void debug_print_format(const char *msg, unsigned fmt ) +{ + debug_printf("%s: %s\n", msg, util_format_name(fmt)); +} +#endif + + + +static const struct debug_named_value pipe_prim_names[] = { +#ifdef DEBUG + DEBUG_NAMED_VALUE(PIPE_PRIM_POINTS), + DEBUG_NAMED_VALUE(PIPE_PRIM_LINES), + DEBUG_NAMED_VALUE(PIPE_PRIM_LINE_LOOP), + DEBUG_NAMED_VALUE(PIPE_PRIM_LINE_STRIP), + DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLES), + DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLE_STRIP), + DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLE_FAN), + DEBUG_NAMED_VALUE(PIPE_PRIM_QUADS), + DEBUG_NAMED_VALUE(PIPE_PRIM_QUAD_STRIP), + DEBUG_NAMED_VALUE(PIPE_PRIM_POLYGON), + DEBUG_NAMED_VALUE(PIPE_PRIM_LINES_ADJACENCY), + DEBUG_NAMED_VALUE(PIPE_PRIM_LINE_STRIP_ADJACENCY), + DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLES_ADJACENCY), + DEBUG_NAMED_VALUE(PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY), +#endif + DEBUG_NAMED_VALUE_END +}; + + +const char *u_prim_name( unsigned prim ) +{ + return debug_dump_enum(pipe_prim_names, prim); +} + + + +#ifdef DEBUG +int fl_indent = 0; +const char* fl_function[1024]; + +int +debug_funclog_enter(const char* f, UNUSED const int line, + UNUSED const char* file) +{ + int i; + + for (i = 0; i < fl_indent; i++) + debug_printf(" "); + debug_printf("%s\n", f); + + assert(fl_indent < 1023); + fl_function[fl_indent++] = f; + + return 0; +} + +void +debug_funclog_exit(const char* f, UNUSED const int line, + UNUSED const char* file) +{ + --fl_indent; + assert(fl_indent >= 0); + assert(fl_function[fl_indent] == f); +} + +void +debug_funclog_enter_exit(const char* f, UNUSED const int line, + UNUSED const char* file) +{ + int i; + for (i = 0; i < fl_indent; i++) + debug_printf(" "); + debug_printf("%s\n", f); +} +#endif + + + +#ifdef DEBUG +/** + * Print PIPE_TRANSFER_x flags with a message. + */ +void +debug_print_transfer_flags(const char *msg, unsigned usage) +{ +#define FLAG(x) { x, #x } + static const struct { + unsigned bit; + const char *name; + } flags[] = { + FLAG(PIPE_TRANSFER_READ), + FLAG(PIPE_TRANSFER_WRITE), + FLAG(PIPE_TRANSFER_MAP_DIRECTLY), + FLAG(PIPE_TRANSFER_DISCARD_RANGE), + FLAG(PIPE_TRANSFER_DONTBLOCK), + FLAG(PIPE_TRANSFER_UNSYNCHRONIZED), + FLAG(PIPE_TRANSFER_FLUSH_EXPLICIT), + FLAG(PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) + }; + unsigned i; + + debug_printf("%s ", msg); + + for (i = 0; i < ARRAY_SIZE(flags); i++) { + if (usage & flags[i].bit) { + debug_printf("%s", flags[i].name); + usage &= ~flags[i].bit; + if (usage) { + debug_printf(" | "); + } + } + } + + debug_printf("\n"); +#undef FLAG +} + + + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug.h new file mode 100644 index 0000000..26fb6bb --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug.h @@ -0,0 +1,460 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Cross-platform debugging helpers. + * + * For now it just has assert and printf replacements, but it might be extended + * with stack trace reports and more advanced logging in the near future. + * + * @author Jose Fonseca + */ + +#ifndef U_DEBUG_H_ +#define U_DEBUG_H_ + + +#include "os/os_misc.h" + +#include "pipe/p_format.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(__GNUC__) +#define _util_printf_format(fmt, list) __attribute__ ((format (printf, fmt, list))) +#else +#define _util_printf_format(fmt, list) +#endif + +void _debug_vprintf(const char *format, va_list ap); + + +static inline void +_debug_printf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + _debug_vprintf(format, ap); + va_end(ap); +} + + +/** + * Print debug messages. + * + * The actual channel used to output debug message is platform specific. To + * avoid misformating or truncation, follow these rules of thumb: + * - output whole lines + * - avoid outputing large strings (512 bytes is the current maximum length + * that is guaranteed to be printed in all platforms) + */ +#if !defined(PIPE_OS_HAIKU) +static inline void +debug_printf(const char *format, ...) _util_printf_format(1,2); + +static inline void +debug_printf(const char *format, ...) +{ +#ifdef DEBUG + va_list ap; + va_start(ap, format); + _debug_vprintf(format, ap); + va_end(ap); +#else + (void) format; /* silence warning */ +#endif +} +#else /* is Haiku */ +/* Haiku provides debug_printf in libroot with OS.h */ +#include +#endif + + +/* + * ... isn't portable so we need to pass arguments in parentheses. + * + * usage: + * debug_printf_once(("answer: %i\n", 42)); + */ +#define debug_printf_once(args) \ + do { \ + static boolean once = TRUE; \ + if (once) { \ + once = FALSE; \ + debug_printf args; \ + } \ + } while (0) + + +#ifdef DEBUG +#define debug_vprintf(_format, _ap) _debug_vprintf(_format, _ap) +#else +#define debug_vprintf(_format, _ap) ((void)0) +#endif + + +#ifdef DEBUG +/** + * Dump a blob in hex to the same place that debug_printf sends its + * messages. + */ +void debug_print_blob( const char *name, const void *blob, unsigned size ); + +/* Print a message along with a prettified format string + */ +void debug_print_format(const char *msg, unsigned fmt ); +#else +#define debug_print_blob(_name, _blob, _size) ((void)0) +#define debug_print_format(_msg, _fmt) ((void)0) +#endif + + +/** + * Disable interactive error message boxes. + * + * Should be called as soon as possible for effectiveness. + */ +void +debug_disable_error_message_boxes(void); + + +/** + * Hard-coded breakpoint. + */ +#ifdef DEBUG +#define debug_break() os_break() +#else /* !DEBUG */ +#define debug_break() ((void)0) +#endif /* !DEBUG */ + + +#ifdef _MSC_VER +__declspec(noreturn) +#endif +void _debug_assert_fail(const char *expr, + const char *file, + unsigned line, + const char *function) +#if defined(__GNUC__) && !defined(DEBUG) + __attribute__((noreturn)) +#endif +; + + +/** + * Assert macro + * + * Do not expect that the assert call terminates -- errors must be handled + * regardless of assert behavior. + * + * For non debug builds the assert macro will expand to a no-op, so do not + * call functions with side effects in the assert expression. + */ +#ifdef DEBUG +#define debug_assert(expr) ((expr) ? (void)0 : _debug_assert_fail(#expr, __FILE__, __LINE__, __FUNCTION__)) +#else +#define debug_assert(expr) (void)(0 && (expr)) +#endif + + +/** Override standard assert macro */ +#ifdef assert +#undef assert +#endif +#define assert(expr) debug_assert(expr) + + +/** + * Output the current function name. + */ +#ifdef DEBUG +#define debug_checkpoint() \ + _debug_printf("%s\n", __FUNCTION__) +#else +#define debug_checkpoint() \ + ((void)0) +#endif + + +/** + * Output the full source code position. + */ +#ifdef DEBUG +#define debug_checkpoint_full() \ + _debug_printf("%s:%u:%s\n", __FILE__, __LINE__, __FUNCTION__) +#else +#define debug_checkpoint_full() \ + ((void)0) +#endif + + +/** + * Output a warning message. Muted on release version. + */ +#ifdef DEBUG +#define debug_warning(__msg) \ + _debug_printf("%s:%u:%s: warning: %s\n", __FILE__, __LINE__, __FUNCTION__, __msg) +#else +#define debug_warning(__msg) \ + ((void)0) +#endif + + +/** + * Emit a warning message, but only once. + */ +#ifdef DEBUG +#define debug_warn_once(__msg) \ + do { \ + static bool warned = FALSE; \ + if (!warned) { \ + _debug_printf("%s:%u:%s: one time warning: %s\n", \ + __FILE__, __LINE__, __FUNCTION__, __msg); \ + warned = TRUE; \ + } \ + } while (0) +#else +#define debug_warn_once(__msg) \ + ((void)0) +#endif + + +/** + * Output an error message. Not muted on release version. + */ +#ifdef DEBUG +#define debug_error(__msg) \ + _debug_printf("%s:%u:%s: error: %s\n", __FILE__, __LINE__, __FUNCTION__, __msg) +#else +#define debug_error(__msg) \ + _debug_printf("error: %s\n", __msg) +#endif + + +/** + * Used by debug_dump_enum and debug_dump_flags to describe symbols. + */ +struct debug_named_value +{ + const char *name; + unsigned long value; + const char *desc; +}; + + +/** + * Some C pre-processor magic to simplify creating named values. + * + * Example: + * @code + * static const debug_named_value my_names[] = { + * DEBUG_NAMED_VALUE(MY_ENUM_VALUE_X), + * DEBUG_NAMED_VALUE(MY_ENUM_VALUE_Y), + * DEBUG_NAMED_VALUE(MY_ENUM_VALUE_Z), + * DEBUG_NAMED_VALUE_END + * }; + * + * ... + * debug_printf("%s = %s\n", + * name, + * debug_dump_enum(my_names, my_value)); + * ... + * @endcode + */ +#define DEBUG_NAMED_VALUE(__symbol) {#__symbol, (unsigned long)__symbol, NULL} +#define DEBUG_NAMED_VALUE_WITH_DESCRIPTION(__symbol, __desc) {#__symbol, (unsigned long)__symbol, __desc} +#define DEBUG_NAMED_VALUE_END {NULL, 0, NULL} + + +/** + * Convert a enum value to a string. + */ +const char * +debug_dump_enum(const struct debug_named_value *names, + unsigned long value); + +const char * +debug_dump_enum_noprefix(const struct debug_named_value *names, + const char *prefix, + unsigned long value); + + +/** + * Convert binary flags value to a string. + */ +const char * +debug_dump_flags(const struct debug_named_value *names, + unsigned long value); + + +/** + * Function enter exit loggers + */ +#ifdef DEBUG +int debug_funclog_enter(const char* f, const int line, const char* file); +void debug_funclog_exit(const char* f, const int line, const char* file); +void debug_funclog_enter_exit(const char* f, const int line, const char* file); + +#define DEBUG_FUNCLOG_ENTER() \ + int __debug_decleration_work_around = \ + debug_funclog_enter(__FUNCTION__, __LINE__, __FILE__) +#define DEBUG_FUNCLOG_EXIT() \ + do { \ + (void)__debug_decleration_work_around; \ + debug_funclog_exit(__FUNCTION__, __LINE__, __FILE__); \ + return; \ + } while(0) +#define DEBUG_FUNCLOG_EXIT_RET(ret) \ + do { \ + (void)__debug_decleration_work_around; \ + debug_funclog_exit(__FUNCTION__, __LINE__, __FILE__); \ + return ret; \ + } while(0) +#define DEBUG_FUNCLOG_ENTER_EXIT() \ + debug_funclog_enter_exit(__FUNCTION__, __LINE__, __FILE__) + +#else +#define DEBUG_FUNCLOG_ENTER() \ + int __debug_decleration_work_around +#define DEBUG_FUNCLOG_EXIT() \ + do { (void)__debug_decleration_work_around; return; } while(0) +#define DEBUG_FUNCLOG_EXIT_RET(ret) \ + do { (void)__debug_decleration_work_around; return ret; } while(0) +#define DEBUG_FUNCLOG_ENTER_EXIT() +#endif + + +/** + * Get option. + * + * It is an alias for getenv on Linux. + * + * On Windows it reads C:\gallium.cfg, which is a text file with CR+LF line + * endings with one option per line as + * + * NAME=value + * + * This file must be terminated with an extra empty line. + */ +const char * +debug_get_option(const char *name, const char *dfault); + +boolean +debug_get_bool_option(const char *name, boolean dfault); + +long +debug_get_num_option(const char *name, long dfault); + +unsigned long +debug_get_flags_option(const char *name, + const struct debug_named_value *flags, + unsigned long dfault); + +#define DEBUG_GET_ONCE_BOOL_OPTION(sufix, name, dfault) \ +static boolean \ +debug_get_option_ ## sufix (void) \ +{ \ + static boolean first = TRUE; \ + static boolean value; \ + if (first) { \ + first = FALSE; \ + value = debug_get_bool_option(name, dfault); \ + } \ + return value; \ +} + +#define DEBUG_GET_ONCE_NUM_OPTION(sufix, name, dfault) \ +static long \ +debug_get_option_ ## sufix (void) \ +{ \ + static boolean first = TRUE; \ + static long value; \ + if (first) { \ + first = FALSE; \ + value = debug_get_num_option(name, dfault); \ + } \ + return value; \ +} + +#define DEBUG_GET_ONCE_FLAGS_OPTION(sufix, name, flags, dfault) \ +static unsigned long \ +debug_get_option_ ## sufix (void) \ +{ \ + static boolean first = TRUE; \ + static unsigned long value; \ + if (first) { \ + first = FALSE; \ + value = debug_get_flags_option(name, flags, dfault); \ + } \ + return value; \ +} + + +unsigned long +debug_memory_begin(void); + +void +debug_memory_end(unsigned long beginning); + + +#ifdef DEBUG +struct pipe_context; +struct pipe_surface; +struct pipe_transfer; +struct pipe_resource; + +void debug_dump_image(const char *prefix, + enum pipe_format format, unsigned cpp, + unsigned width, unsigned height, + unsigned stride, + const void *data); +void debug_dump_surface(struct pipe_context *pipe, + const char *prefix, + struct pipe_surface *surface); +void debug_dump_texture(struct pipe_context *pipe, + const char *prefix, + struct pipe_resource *texture); +#else +#define debug_dump_image(prefix, format, cpp, width, height, stride, data) ((void)0) +#define debug_dump_surface(pipe, prefix, surface) ((void)0) +#endif + + +void +debug_print_transfer_flags(const char *msg, unsigned usage); + + +#ifdef __cplusplus +} +#endif + +#endif /* U_DEBUG_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_describe.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_describe.c new file mode 100644 index 0000000..499c360 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_describe.c @@ -0,0 +1,91 @@ +/************************************************************************** + * + * Copyright 2010 Luca Barbieri + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "pipe/p_state.h" +#include "util/u_format.h" +#include "util/u_debug_describe.h" +#include "util/u_string.h" + +void +debug_describe_reference(char* buf, UNUSED const struct pipe_reference*ptr) +{ + strcpy(buf, "pipe_object"); +} + +void +debug_describe_resource(char* buf, const struct pipe_resource *ptr) +{ + switch(ptr->target) + { + case PIPE_BUFFER: + util_sprintf(buf, "pipe_buffer<%u>", (unsigned)util_format_get_stride(ptr->format, ptr->width0)); + break; + case PIPE_TEXTURE_1D: + util_sprintf(buf, "pipe_texture1d<%u,%s,%u>", ptr->width0, util_format_short_name(ptr->format), ptr->last_level); + break; + case PIPE_TEXTURE_2D: + util_sprintf(buf, "pipe_texture2d<%u,%u,%s,%u>", ptr->width0, ptr->height0, util_format_short_name(ptr->format), ptr->last_level); + break; + case PIPE_TEXTURE_RECT: + util_sprintf(buf, "pipe_texture_rect<%u,%u,%s>", ptr->width0, ptr->height0, util_format_short_name(ptr->format)); + break; + case PIPE_TEXTURE_CUBE: + util_sprintf(buf, "pipe_texture_cube<%u,%u,%s,%u>", ptr->width0, ptr->height0, util_format_short_name(ptr->format), ptr->last_level); + break; + case PIPE_TEXTURE_3D: + util_sprintf(buf, "pipe_texture3d<%u,%u,%u,%s,%u>", ptr->width0, ptr->height0, ptr->depth0, util_format_short_name(ptr->format), ptr->last_level); + break; + default: + util_sprintf(buf, "pipe_martian_resource<%u>", ptr->target); + break; + } +} + +void +debug_describe_surface(char* buf, const struct pipe_surface *ptr) +{ + char res[128]; + debug_describe_resource(res, ptr->texture); + util_sprintf(buf, "pipe_surface<%s,%u,%u,%u>", res, ptr->u.tex.level, ptr->u.tex.first_layer, ptr->u.tex.last_layer); +} + +void +debug_describe_sampler_view(char* buf, const struct pipe_sampler_view *ptr) +{ + char res[128]; + debug_describe_resource(res, ptr->texture); + util_sprintf(buf, "pipe_sampler_view<%s,%s>", res, util_format_short_name(ptr->format)); +} + +void +debug_describe_so_target(char* buf, + const struct pipe_stream_output_target *ptr) +{ + char res[128]; + debug_describe_resource(res, ptr->buffer); + util_sprintf(buf, "pipe_stream_output_target<%s,%u,%u>", res, + ptr->buffer_offset, ptr->buffer_size); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_describe.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_describe.h new file mode 100644 index 0000000..4f7882b --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_describe.h @@ -0,0 +1,51 @@ +/************************************************************************** + * + * Copyright 2010 Luca Barbieri + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_DEBUG_DESCRIBE_H_ +#define U_DEBUG_DESCRIBE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct pipe_reference; +struct pipe_resource; +struct pipe_surface; +struct pipe_sampler_view; + +/* a 256-byte buffer is necessary and sufficient */ +void debug_describe_reference(char* buf, const struct pipe_reference*ptr); +void debug_describe_resource(char* buf, const struct pipe_resource *ptr); +void debug_describe_surface(char* buf, const struct pipe_surface *ptr); +void debug_describe_sampler_view(char* buf, const struct pipe_sampler_view *ptr); +void debug_describe_so_target(char* buf, + const struct pipe_stream_output_target *ptr); + +#ifdef __cplusplus +} +#endif + +#endif /* U_DEBUG_DESCRIBE_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_refcnt.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_refcnt.h new file mode 100644 index 0000000..8f42319 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_debug_refcnt.h @@ -0,0 +1,51 @@ +/************************************************************************** + * + * Copyright 2010 Luca Barbieri + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_DEBUG_REFCNT_H_ +#define U_DEBUG_REFCNT_H_ + +#include "pipe/p_config.h" +#include "pipe/p_state.h" + +#include "util/u_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*debug_reference_descriptor)(char*, const struct pipe_reference*); + +static inline void +debug_reference(UNUSED const struct pipe_reference* p, + UNUSED debug_reference_descriptor get_desc, UNUSED int change) +{ +} + +#ifdef __cplusplus +} +#endif + +#endif /* U_DEBUG_REFCNT_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_double_list.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_double_list.h new file mode 100644 index 0000000..01b87e3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_double_list.h @@ -0,0 +1,147 @@ +/************************************************************************** + * + * Copyright 2006 VMware, Inc., Bismarck, ND. USA. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + **************************************************************************/ + +/** + * \file + * List macros heavily inspired by the Linux kernel + * list handling. No list looping yet. + * + * Is not threadsafe, so common operations need to + * be protected using an external mutex. + */ + +#ifndef _U_DOUBLE_LIST_H_ +#define _U_DOUBLE_LIST_H_ + + +#include +#include "pipe/p_compiler.h" + + +struct list_head +{ + struct list_head *prev; + struct list_head *next; +}; + +static inline void list_inithead(struct list_head *item) +{ + item->prev = item; + item->next = item; +} + +static inline void list_add(struct list_head *item, struct list_head *list) +{ + item->prev = list; + item->next = list->next; + list->next->prev = item; + list->next = item; +} + +static inline void list_addtail(struct list_head *item, struct list_head *list) +{ + item->next = list; + item->prev = list->prev; + list->prev->next = item; + list->prev = item; +} + +static inline void list_replace(struct list_head *from, struct list_head *to) +{ + to->prev = from->prev; + to->next = from->next; + from->next->prev = to; + from->prev->next = to; +} + +static inline void list_del(struct list_head *item) +{ + item->prev->next = item->next; + item->next->prev = item->prev; + item->prev = item->next = NULL; +} + +static inline void list_delinit(struct list_head *item) +{ + item->prev->next = item->next; + item->next->prev = item->prev; + item->next = item; + item->prev = item; +} + +#define LIST_INITHEAD(__item) list_inithead(__item) +#define LIST_ADD(__item, __list) list_add(__item, __list) +#define LIST_ADDTAIL(__item, __list) list_addtail(__item, __list) +#define LIST_REPLACE(__from, __to) list_replace(__from, __to) +#define LIST_DEL(__item) list_del(__item) +#define LIST_DELINIT(__item) list_delinit(__item) + +#define LIST_ENTRY(__type, __item, __field) \ + ((__type *)(((char *)(__item)) - offsetof(__type, __field))) + +#define LIST_IS_EMPTY(__list) \ + ((__list)->next == (__list)) + +/** + * Cast from a pointer to a member of a struct back to the containing struct. + * + * 'sample' MUST be initialized, or else the result is undefined! + */ +#ifndef container_of +#define container_of(ptr, sample, member) \ + (void *)((char *)(ptr) \ + - ((char *)&(sample)->member - (char *)(sample))) +#endif + +#define LIST_FOR_EACH_ENTRY(pos, head, member) \ + for (pos = NULL, pos = container_of((head)->next, pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.next, pos, member)) + +#define LIST_FOR_EACH_ENTRY_SAFE(pos, storage, head, member) \ + for (pos = NULL, pos = container_of((head)->next, pos, member), \ + storage = container_of(pos->member.next, pos, member); \ + &pos->member != (head); \ + pos = storage, storage = container_of(storage->member.next, storage, member)) + +#define LIST_FOR_EACH_ENTRY_SAFE_REV(pos, storage, head, member) \ + for (pos = NULL, pos = container_of((head)->prev, pos, member), \ + storage = container_of(pos->member.prev, pos, member); \ + &pos->member != (head); \ + pos = storage, storage = container_of(storage->member.prev, storage, member)) + +#define LIST_FOR_EACH_ENTRY_FROM(pos, start, head, member) \ + for (pos = NULL, pos = container_of((start), pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.next, pos, member)) + +#define LIST_FOR_EACH_ENTRY_FROM_REV(pos, start, head, member) \ + for (pos = NULL, pos = container_of((start), pos, member); \ + &pos->member != (head); \ + pos = container_of(pos->member.prev, pos, member)) + +#endif /*_U_DOUBLE_LIST_H_*/ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_dual_blend.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_dual_blend.h new file mode 100644 index 0000000..9450800 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_dual_blend.h @@ -0,0 +1,26 @@ +#ifndef U_DUAL_BLEND_H +#define U_DUAL_BLEND_H + +#include "pipe/p_state.h" + +static inline boolean util_blend_factor_is_dual_src(int factor) +{ + return (factor == PIPE_BLENDFACTOR_SRC1_COLOR) || + (factor == PIPE_BLENDFACTOR_SRC1_ALPHA) || + (factor == PIPE_BLENDFACTOR_INV_SRC1_COLOR) || + (factor == PIPE_BLENDFACTOR_INV_SRC1_ALPHA); +} + +static inline boolean util_blend_state_is_dual(const struct pipe_blend_state *blend, + int index) +{ + if (util_blend_factor_is_dual_src(blend->rt[index].rgb_src_factor) || + util_blend_factor_is_dual_src(blend->rt[index].alpha_src_factor) || + util_blend_factor_is_dual_src(blend->rt[index].rgb_dst_factor) || + util_blend_factor_is_dual_src(blend->rt[index].alpha_dst_factor)) + return true; + return false; +} + + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.c new file mode 100644 index 0000000..cda6aa7 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.c @@ -0,0 +1,479 @@ +/************************************************************************** + * + * Copyright 2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Pixel format accessor functions. + * + * @author Jose Fonseca + */ + +#include "u_math.h" +#include "u_memory.h" +#include "u_format.h" +#include "u_format_s3tc.h" +#include "u_surface.h" + +#include "pipe/p_defines.h" + +boolean util_format_s3tc_enabled = FALSE; + +boolean +util_format_is_float(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + int i; + + assert(desc); + if (!desc) { + return FALSE; + } + + i = util_format_get_first_non_void_channel(format); + if (i == -1) { + return FALSE; + } + + return desc->channel[i].type == UTIL_FORMAT_TYPE_FLOAT ? TRUE : FALSE; +} + + +/** Test if the format contains RGB, but not alpha */ +boolean +util_format_has_alpha(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + return (desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB || + desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) && + desc->swizzle[3] != UTIL_FORMAT_SWIZZLE_1; +} + + +boolean +util_format_is_luminance(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB || + desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) && + desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_1) { + return TRUE; + } + return FALSE; +} + +boolean +util_format_is_alpha(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB || + desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) && + desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_0 && + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_0 && + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_0 && + desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_X) { + return TRUE; + } + return FALSE; +} + +boolean +util_format_is_pure_integer(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + int i; + + /* Find the first non-void channel. */ + i = util_format_get_first_non_void_channel(format); + if (i == -1) + return FALSE; + + return desc->channel[i].pure_integer ? TRUE : FALSE; +} + +boolean +util_format_is_pure_sint(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + int i; + + i = util_format_get_first_non_void_channel(format); + if (i == -1) + return FALSE; + + return (desc->channel[i].type == UTIL_FORMAT_TYPE_SIGNED && desc->channel[i].pure_integer) ? TRUE : FALSE; +} + +boolean +util_format_is_pure_uint(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + int i; + + i = util_format_get_first_non_void_channel(format); + if (i == -1) + return FALSE; + + return (desc->channel[i].type == UTIL_FORMAT_TYPE_UNSIGNED && desc->channel[i].pure_integer) ? TRUE : FALSE; +} + +/** + * Returns true if all non-void channels are normalized signed. + */ +boolean +util_format_is_snorm(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + int i; + + if (desc->is_mixed) + return FALSE; + + i = util_format_get_first_non_void_channel(format); + if (i == -1) + return FALSE; + + return desc->channel[i].type == UTIL_FORMAT_TYPE_SIGNED && + !desc->channel[i].pure_integer && + desc->channel[i].normalized; +} + +boolean +util_format_is_luminance_alpha(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB || + desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) && + desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_Y) { + return TRUE; + } + return FALSE; +} + + +boolean +util_format_is_intensity(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + if ((desc->colorspace == UTIL_FORMAT_COLORSPACE_RGB || + desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) && + desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[1] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[2] == UTIL_FORMAT_SWIZZLE_X && + desc->swizzle[3] == UTIL_FORMAT_SWIZZLE_X) { + return TRUE; + } + return FALSE; +} + +boolean +util_format_is_subsampled_422(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + return desc->layout == UTIL_FORMAT_LAYOUT_SUBSAMPLED && + desc->block.width == 2 && + desc->block.height == 1 && + desc->block.bits == 32; +} + +boolean +util_format_is_supported(enum pipe_format format, unsigned bind) +{ + if (util_format_is_s3tc(format) && !util_format_s3tc_enabled) { + return FALSE; + } + +#ifndef TEXTURE_FLOAT_ENABLED + if ((bind & PIPE_BIND_RENDER_TARGET) && + format != PIPE_FORMAT_R9G9B9E5_FLOAT && + format != PIPE_FORMAT_R11G11B10_FLOAT && + util_format_is_float(format)) { + return FALSE; + } +#endif + + return TRUE; +} + + +/** + * Calculates the MRD for the depth format. MRD is used in depth bias + * for UNORM and unbound depth buffers. When the depth buffer is floating + * point, the depth bias calculation does not use the MRD. However, the + * default MRD will be 1.0 / ((1 << 24) - 1). + */ +double +util_get_depth_format_mrd(const struct util_format_description *desc) +{ + /* + * Depth buffer formats without a depth component OR scenarios + * without a bound depth buffer default to D24. + */ + double mrd = 1.0 / ((1 << 24) - 1); + unsigned depth_channel; + + assert(desc); + + /* + * Some depth formats do not store the depth component in the first + * channel, detect the format and adjust the depth channel. Get the + * swizzled depth component channel. + */ + depth_channel = desc->swizzle[0]; + + if (desc->channel[depth_channel].type == UTIL_FORMAT_TYPE_UNSIGNED && + desc->channel[depth_channel].normalized) { + int depth_bits; + + depth_bits = desc->channel[depth_channel].size; + mrd = 1.0 / ((1ULL << depth_bits) - 1); + } + + return mrd; +} + +boolean +util_is_format_compatible(const struct util_format_description *src_desc, + const struct util_format_description *dst_desc) +{ + unsigned chan; + + if (src_desc->format == dst_desc->format) { + return TRUE; + } + + if (src_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN || + dst_desc->layout != UTIL_FORMAT_LAYOUT_PLAIN) { + return FALSE; + } + + if (src_desc->block.bits != dst_desc->block.bits || + src_desc->nr_channels != dst_desc->nr_channels || + src_desc->colorspace != dst_desc->colorspace) { + return FALSE; + } + + for (chan = 0; chan < 4; ++chan) { + if (src_desc->channel[chan].size != + dst_desc->channel[chan].size) { + return FALSE; + } + } + + for (chan = 0; chan < 4; ++chan) { + enum util_format_swizzle swizzle = dst_desc->swizzle[chan]; + + if (swizzle < 4) { + if (src_desc->swizzle[chan] != swizzle) { + return FALSE; + } + if ((src_desc->channel[swizzle].type != + dst_desc->channel[swizzle].type) || + (src_desc->channel[swizzle].normalized != + dst_desc->channel[swizzle].normalized)) { + return FALSE; + } + } + } + + return TRUE; +} + + +boolean +util_format_fits_8unorm(const struct util_format_description *format_desc) +{ + unsigned chan; + + /* + * After linearized sRGB values require more than 8bits. + */ + + if (format_desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) { + return FALSE; + } + + switch (format_desc->layout) { + + case UTIL_FORMAT_LAYOUT_S3TC: + /* + * These are straight forward. + */ + return TRUE; + case UTIL_FORMAT_LAYOUT_RGTC: + if (format_desc->format == PIPE_FORMAT_RGTC1_SNORM || + format_desc->format == PIPE_FORMAT_RGTC2_SNORM || + format_desc->format == PIPE_FORMAT_LATC1_SNORM || + format_desc->format == PIPE_FORMAT_LATC2_SNORM) + return FALSE; + return TRUE; + case UTIL_FORMAT_LAYOUT_BPTC: + if (format_desc->format == PIPE_FORMAT_BPTC_RGBA_UNORM) + return TRUE; + return FALSE; + + case UTIL_FORMAT_LAYOUT_PLAIN: + /* + * For these we can find a generic rule. + */ + + for (chan = 0; chan < format_desc->nr_channels; ++chan) { + switch (format_desc->channel[chan].type) { + case UTIL_FORMAT_TYPE_VOID: + break; + case UTIL_FORMAT_TYPE_UNSIGNED: + if (!format_desc->channel[chan].normalized || + format_desc->channel[chan].size > 8) { + return FALSE; + } + break; + default: + return FALSE; + } + } + return TRUE; + + default: + /* + * Handle all others on a case by case basis. + */ + + switch (format_desc->format) { + case PIPE_FORMAT_R1_UNORM: + case PIPE_FORMAT_UYVY: + case PIPE_FORMAT_YUYV: + case PIPE_FORMAT_R8G8_B8G8_UNORM: + case PIPE_FORMAT_G8R8_G8B8_UNORM: + return TRUE; + + default: + return FALSE; + } + } +} + + +void util_format_compose_swizzles(const unsigned char swz1[4], + const unsigned char swz2[4], + unsigned char dst[4]) +{ + unsigned i; + + for (i = 0; i < 4; i++) { + dst[i] = swz2[i] <= UTIL_FORMAT_SWIZZLE_W ? + swz1[swz2[i]] : swz2[i]; + } +} + +void util_format_apply_color_swizzle(union pipe_color_union *dst, + const union pipe_color_union *src, + const unsigned char swz[4], + const boolean is_integer) +{ + unsigned c; + + if (is_integer) { + for (c = 0; c < 4; ++c) { + switch (swz[c]) { + case PIPE_SWIZZLE_RED: dst->ui[c] = src->ui[0]; break; + case PIPE_SWIZZLE_GREEN: dst->ui[c] = src->ui[1]; break; + case PIPE_SWIZZLE_BLUE: dst->ui[c] = src->ui[2]; break; + case PIPE_SWIZZLE_ALPHA: dst->ui[c] = src->ui[3]; break; + default: + dst->ui[c] = (swz[c] == PIPE_SWIZZLE_ONE) ? 1 : 0; + break; + } + } + } else { + for (c = 0; c < 4; ++c) { + switch (swz[c]) { + case PIPE_SWIZZLE_RED: dst->f[c] = src->f[0]; break; + case PIPE_SWIZZLE_GREEN: dst->f[c] = src->f[1]; break; + case PIPE_SWIZZLE_BLUE: dst->f[c] = src->f[2]; break; + case PIPE_SWIZZLE_ALPHA: dst->f[c] = src->f[3]; break; + default: + dst->f[c] = (swz[c] == PIPE_SWIZZLE_ONE) ? 1.0f : 0.0f; + break; + } + } + } +} + +void util_format_swizzle_4f(float *dst, const float *src, + const unsigned char swz[4]) +{ + unsigned i; + + for (i = 0; i < 4; i++) { + if (swz[i] <= UTIL_FORMAT_SWIZZLE_W) + dst[i] = src[swz[i]]; + else if (swz[i] == UTIL_FORMAT_SWIZZLE_0) + dst[i] = 0; + else if (swz[i] == UTIL_FORMAT_SWIZZLE_1) + dst[i] = 1; + } +} + +void util_format_unswizzle_4f(float *dst, const float *src, + const unsigned char swz[4]) +{ + unsigned i; + + for (i = 0; i < 4; i++) { + switch (swz[i]) { + case UTIL_FORMAT_SWIZZLE_X: + dst[0] = src[i]; + break; + case UTIL_FORMAT_SWIZZLE_Y: + dst[1] = src[i]; + break; + case UTIL_FORMAT_SWIZZLE_Z: + dst[2] = src[i]; + break; + case UTIL_FORMAT_SWIZZLE_W: + dst[3] = src[i]; + break; + } + } +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.csv b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.csv new file mode 100644 index 0000000..a20ecb3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.csv @@ -0,0 +1,401 @@ +########################################################################### +# +# Copyright 2009-2010 VMware, Inc. +# All Rights Reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sub license, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice (including the +# next paragraph) shall be included in all copies or substantial portions +# of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR +# ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +########################################################################### + +# This CSV file has the input data for u_format.h's struct +# util_format_description. +# +# Each format entry contains: +# - name, per enum pipe_format +# - layout, per enum util_format_layout, in shortened lower caps +# - pixel block's width +# - pixel block's height +# - channel encoding (only meaningful for plain layout), containing for each +# channel the following information: +# - type, one of +# - 'x': void +# - 'u': unsigned +# - 's': signed +# - 'h': fixed +# - 'f': FLOAT +# - optionally followed by 'n' if it is normalized +# - optionally followed by 'p' if it is pure +# - number of bits +# - channel swizzle +# - color space: rgb, yub, sz +# - (optional) channel encoding for big-endian targets +# - (optional) channel swizzle for big-endian targets +# +# See also: +# - http://msdn.microsoft.com/en-us/library/bb172558.aspx (D3D9) +# - http://msdn.microsoft.com/en-us/library/bb205073.aspx#mapping_texture_formats (D3D9 -> D3D10) +# - http://msdn.microsoft.com/en-us/library/bb173059.aspx (D3D10) +# +# Note that GL doesn't really specify the layout of internal formats. See +# OpenGL 2.1 specification, Table 3.16, on the "Correspondence of sized +# internal formats to base in- ternal formats, and desired component +# resolutions for each sized internal format." + +# None +# Described as regular uint_8 bytes, i.e. PIPE_FORMAT_R8_USCALED +PIPE_FORMAT_NONE , plain, 1, 1, u8 , , , , x001, rgb + +# Typical rendertarget formats +PIPE_FORMAT_B8G8R8A8_UNORM , plain, 1, 1, un8 , un8 , un8 , un8 , zyxw, rgb +PIPE_FORMAT_B8G8R8X8_UNORM , plain, 1, 1, un8 , un8 , un8 , x8 , zyx1, rgb +PIPE_FORMAT_A8R8G8B8_UNORM , plain, 1, 1, un8 , un8 , un8 , un8 , yzwx, rgb +PIPE_FORMAT_X8R8G8B8_UNORM , plain, 1, 1, x8 , un8 , un8 , un8 , yzw1, rgb +PIPE_FORMAT_A8B8G8R8_UNORM , plain, 1, 1, un8 , un8 , un8 , un8 , wzyx, rgb +PIPE_FORMAT_X8B8G8R8_UNORM , plain, 1, 1, x8 , un8 , un8 , un8 , wzy1, rgb +# PIPE_FORMAT_R8G8B8A8_UNORM is below +PIPE_FORMAT_R8G8B8X8_UNORM , plain, 1, 1, un8 , un8 , un8 , x8 , xyz1, rgb +PIPE_FORMAT_B5G5R5X1_UNORM , plain, 1, 1, un5 , un5 , un5 , x1 , zyx1, rgb, x1 , un5 , un5 , un5 , yzw1 +PIPE_FORMAT_B5G5R5A1_UNORM , plain, 1, 1, un5 , un5 , un5 , un1 , zyxw, rgb, un1 , un5 , un5 , un5 , yzwx +PIPE_FORMAT_B4G4R4A4_UNORM , plain, 1, 1, un4 , un4 , un4 , un4 , zyxw, rgb, un4 , un4 , un4 , un4 , yzwx +PIPE_FORMAT_B4G4R4X4_UNORM , plain, 1, 1, un4 , un4 , un4 , x4 , zyx1, rgb, x4 , un4 , un4 , un4 , yzw1 +PIPE_FORMAT_A4B4G4R4_UNORM , plain, 1, 1, un4 , un4 , un4 , un4 , wzyx, rgb, un4 , un4 , un4 , un4 , xyzw +PIPE_FORMAT_B5G6R5_UNORM , plain, 1, 1, un5 , un6 , un5 , , zyx1, rgb, un5 , un6 , un5 , , xyz1 +PIPE_FORMAT_R10G10B10A2_UNORM , plain, 1, 1, un10, un10, un10, un2 , xyzw, rgb, un2 , un10, un10, un10, wzyx +PIPE_FORMAT_R10G10B10X2_UNORM , plain, 1, 1, un10, un10, un10, x2, xyz1, rgb, x2 , un10, un10, un10, wzy1 +PIPE_FORMAT_B10G10R10A2_UNORM , plain, 1, 1, un10, un10, un10, un2 , zyxw, rgb, un2 , un10, un10, un10, yzwx +PIPE_FORMAT_B2G3R3_UNORM , plain, 1, 1, un2 , un3 , un3 , , zyx1, rgb, un3 , un3 , un2 , , xyz1 + +# Luminance/Intensity/Alpha formats +PIPE_FORMAT_L8_UNORM , plain, 1, 1, un8 , , , , xxx1, rgb +PIPE_FORMAT_A8_UNORM , plain, 1, 1, un8 , , , , 000x, rgb +PIPE_FORMAT_I8_UNORM , plain, 1, 1, un8 , , , , xxxx, rgb +PIPE_FORMAT_L4A4_UNORM , plain, 1, 1, un4 , un4 , , , xxxy, rgb, un4 , un4 , , , yyyx +PIPE_FORMAT_L8A8_UNORM , plain, 1, 1, un8 , un8 , , , xxxy, rgb +PIPE_FORMAT_L16_UNORM , plain, 1, 1, un16, , , , xxx1, rgb +PIPE_FORMAT_A16_UNORM , plain, 1, 1, un16, , , , 000x, rgb +PIPE_FORMAT_I16_UNORM , plain, 1, 1, un16, , , , xxxx, rgb +PIPE_FORMAT_L16A16_UNORM , plain, 1, 1, un16, un16, , , xxxy, rgb +PIPE_FORMAT_A8_SNORM , plain, 1, 1, sn8 , , , , 000x, rgb +PIPE_FORMAT_L8_SNORM , plain, 1, 1, sn8 , , , , xxx1, rgb +PIPE_FORMAT_L8A8_SNORM , plain, 1, 1, sn8 , sn8 , , , xxxy, rgb +PIPE_FORMAT_I8_SNORM , plain, 1, 1, sn8 , , , , xxxx, rgb +PIPE_FORMAT_A16_SNORM , plain, 1, 1, sn16, , , , 000x, rgb +PIPE_FORMAT_L16_SNORM , plain, 1, 1, sn16, , , , xxx1, rgb +PIPE_FORMAT_L16A16_SNORM , plain, 1, 1, sn16, sn16, , , xxxy, rgb +PIPE_FORMAT_I16_SNORM , plain, 1, 1, sn16, , , , xxxx, rgb +PIPE_FORMAT_A16_FLOAT , plain, 1, 1, f16 , , , , 000x, rgb +PIPE_FORMAT_L16_FLOAT , plain, 1, 1, f16 , , , , xxx1, rgb +PIPE_FORMAT_L16A16_FLOAT , plain, 1, 1, f16 , f16 , , , xxxy, rgb +PIPE_FORMAT_I16_FLOAT , plain, 1, 1, f16 , , , , xxxx, rgb +PIPE_FORMAT_A32_FLOAT , plain, 1, 1, f32 , , , , 000x, rgb +PIPE_FORMAT_L32_FLOAT , plain, 1, 1, f32 , , , , xxx1, rgb +PIPE_FORMAT_L32A32_FLOAT , plain, 1, 1, f32 , f32 , , , xxxy, rgb +PIPE_FORMAT_I32_FLOAT , plain, 1, 1, f32 , , , , xxxx, rgb + +# SRGB formats +PIPE_FORMAT_L8_SRGB , plain, 1, 1, un8 , , , , xxx1, srgb +PIPE_FORMAT_R8_SRGB , plain, 1, 1, un8 , , , , x001, srgb +PIPE_FORMAT_L8A8_SRGB , plain, 1, 1, un8 , un8 , , , xxxy, srgb +PIPE_FORMAT_R8G8B8_SRGB , plain, 1, 1, un8 , un8 , un8 , , xyz1, srgb +PIPE_FORMAT_R8G8B8A8_SRGB , plain, 1, 1, un8 , un8 , un8 , un8 , xyzw, srgb +PIPE_FORMAT_A8B8G8R8_SRGB , plain, 1, 1, un8 , un8 , un8 , un8 , wzyx, srgb +PIPE_FORMAT_X8B8G8R8_SRGB , plain, 1, 1, x8 , un8 , un8 , un8 , wzy1, srgb +PIPE_FORMAT_B8G8R8A8_SRGB , plain, 1, 1, un8 , un8 , un8 , un8 , zyxw, srgb +PIPE_FORMAT_B8G8R8X8_SRGB , plain, 1, 1, un8 , un8 , un8 , x8 , zyx1, srgb +PIPE_FORMAT_A8R8G8B8_SRGB , plain, 1, 1, un8 , un8 , un8 , un8 , yzwx, srgb +PIPE_FORMAT_X8R8G8B8_SRGB , plain, 1, 1, x8 , un8 , un8 , un8 , yzw1, srgb + +# Mixed-sign formats (typically used for bump map textures) +PIPE_FORMAT_R8SG8SB8UX8U_NORM , plain, 1, 1, sn8 , sn8 , un8 , x8 , xyz1, rgb +PIPE_FORMAT_R10SG10SB10SA2U_NORM , plain, 1, 1, sn10, sn10, sn10, un2 , xyzw, rgb, un2 , sn10, sn10, sn10, wzyx +PIPE_FORMAT_R5SG5SB6U_NORM , plain, 1, 1, sn5 , sn5 , un6 , , xyz1, rgb, un6 , sn5 , sn5 , , zyx1 + +# Depth-stencil formats +PIPE_FORMAT_S8_UINT , plain, 1, 1, up8 , , , , _x__, zs +PIPE_FORMAT_Z16_UNORM , plain, 1, 1, un16, , , , x___, zs +PIPE_FORMAT_Z32_UNORM , plain, 1, 1, un32, , , , x___, zs +PIPE_FORMAT_Z32_FLOAT , plain, 1, 1, f32 , , , , x___, zs +PIPE_FORMAT_Z24_UNORM_S8_UINT , plain, 1, 1, un24, up8 , , , xy__, zs, up8 , un24, , , yx__ +PIPE_FORMAT_S8_UINT_Z24_UNORM , plain, 1, 1, up8 , un24, , , yx__, zs, un24, up8 , , , xy__ +PIPE_FORMAT_X24S8_UINT , plain, 1, 1, x24 , up8 , , , _y__, zs, up8 , x24 , , , _x__ +PIPE_FORMAT_S8X24_UINT , plain, 1, 1, up8 , x24 , , , _x__, zs, x24 , up8 , , , _y__ +PIPE_FORMAT_Z24X8_UNORM , plain, 1, 1, un24, x8 , , , x___, zs, x8 , un24, , , y___ +PIPE_FORMAT_X8Z24_UNORM , plain, 1, 1, x8 , un24, , , y___, zs, un24, x8 , , , x___ +PIPE_FORMAT_Z32_FLOAT_S8X24_UINT , plain, 1, 1, f32 , up8 , x24, , xy__, zs, f32 , x24 , up8, , xz__ +PIPE_FORMAT_X32_S8X24_UINT , plain, 1, 1, x32 , up8 , x24, , _y__, zs, x32 , x24 , up8, , _z__ + +# YUV formats +# http://www.fourcc.org/yuv.php#UYVY +PIPE_FORMAT_UYVY , subsampled, 2, 1, x32 , , , , xyz1, yuv +# http://www.fourcc.org/yuv.php#YUYV (a.k.a http://www.fourcc.org/yuv.php#YUY2) +PIPE_FORMAT_YUYV , subsampled, 2, 1, x32 , , , , xyz1, yuv +# same subsampling but with rgb channels +PIPE_FORMAT_R8G8_B8G8_UNORM , subsampled, 2, 1, x32 , , , , xyz1, rgb +PIPE_FORMAT_G8R8_G8B8_UNORM , subsampled, 2, 1, x32 , , , , xyz1, rgb +PIPE_FORMAT_G8R8_B8R8_UNORM , subsampled, 2, 1, x32 , , , , yxz1, rgb +PIPE_FORMAT_R8G8_R8B8_UNORM , subsampled, 2, 1, x32 , , , , yxz1, rgb + +# some special formats not fitting anywhere else +PIPE_FORMAT_R11G11B10_FLOAT , other, 1, 1, x32 , , , , xyz1, rgb +PIPE_FORMAT_R9G9B9E5_FLOAT , other, 1, 1, x32 , , , , xyz1, rgb +PIPE_FORMAT_R1_UNORM , other, 8, 1, x8 , , , , x001, rgb +# A.k.a. D3DFMT_CxV8U8 +PIPE_FORMAT_R8G8Bx_SNORM , other, 1, 1, sn8 , sn8 , , , xyz1, rgb + +# Compressed formats +# - http://en.wikipedia.org/wiki/S3_Texture_Compression +# - http://www.opengl.org/registry/specs/EXT/texture_compression_s3tc.txt +# - http://www.opengl.org/registry/specs/ARB/texture_compression_rgtc.txt +# - http://www.opengl.org/registry/specs/EXT/texture_compression_latc.txt +# - http://www.opengl.org/registry/specs/ARB/texture_compression_bptc.txt +# - http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt +# - http://msdn.microsoft.com/en-us/library/bb694531.aspx +PIPE_FORMAT_DXT1_RGB , s3tc, 4, 4, x64 , , , , xyz1, rgb +PIPE_FORMAT_DXT1_RGBA , s3tc, 4, 4, x64 , , , , xyzw, rgb +PIPE_FORMAT_DXT3_RGBA , s3tc, 4, 4, x128, , , , xyzw, rgb +PIPE_FORMAT_DXT5_RGBA , s3tc, 4, 4, x128, , , , xyzw, rgb +PIPE_FORMAT_DXT1_SRGB , s3tc, 4, 4, x64 , , , , xyz1, srgb +PIPE_FORMAT_DXT1_SRGBA , s3tc, 4, 4, x64 , , , , xyzw, srgb +PIPE_FORMAT_DXT3_SRGBA , s3tc, 4, 4, x128, , , , xyzw, srgb +PIPE_FORMAT_DXT5_SRGBA , s3tc, 4, 4, x128, , , , xyzw, srgb + +PIPE_FORMAT_RGTC1_UNORM , rgtc, 4, 4, x64, , , , x001, rgb +PIPE_FORMAT_RGTC1_SNORM , rgtc, 4, 4, x64, , , , x001, rgb +PIPE_FORMAT_RGTC2_UNORM , rgtc, 4, 4, x128, , , , xy01, rgb +PIPE_FORMAT_RGTC2_SNORM , rgtc, 4, 4, x128, , , , xy01, rgb + +PIPE_FORMAT_LATC1_UNORM , rgtc, 4, 4, x64, , , , xxx1, rgb +PIPE_FORMAT_LATC1_SNORM , rgtc, 4, 4, x64, , , , xxx1, rgb +PIPE_FORMAT_LATC2_UNORM , rgtc, 4, 4, x128, , , , xxxy, rgb +PIPE_FORMAT_LATC2_SNORM , rgtc, 4, 4, x128, , , , xxxy, rgb + +PIPE_FORMAT_ETC1_RGB8 , etc, 4, 4, x64, , , , xyz1, rgb + +PIPE_FORMAT_BPTC_RGBA_UNORM , bptc, 4, 4, x128, , , , xyzw, rgb +PIPE_FORMAT_BPTC_SRGBA , bptc, 4, 4, x128, , , , xyzw, srgb +PIPE_FORMAT_BPTC_RGB_FLOAT , bptc, 4, 4, x128, , , , xyz1, rgb +PIPE_FORMAT_BPTC_RGB_UFLOAT , bptc, 4, 4, x128, , , , xyz1, rgb + +# Straightforward D3D10-like formats (also used for +# vertex buffer element description) +# +# See also: +# - src/gallium/auxiliary/translate/translate_generic.c +# - src/mesa/state_tracker/st_draw.c +PIPE_FORMAT_R64_FLOAT , plain, 1, 1, f64 , , , , x001, rgb +PIPE_FORMAT_R64G64_FLOAT , plain, 1, 1, f64 , f64 , , , xy01, rgb +PIPE_FORMAT_R64G64B64_FLOAT , plain, 1, 1, f64 , f64 , f64 , , xyz1, rgb +PIPE_FORMAT_R64G64B64A64_FLOAT , plain, 1, 1, f64 , f64 , f64 , f64 , xyzw, rgb +PIPE_FORMAT_R32_FLOAT , plain, 1, 1, f32 , , , , x001, rgb +PIPE_FORMAT_R32G32_FLOAT , plain, 1, 1, f32 , f32 , , , xy01, rgb +PIPE_FORMAT_R32G32B32_FLOAT , plain, 1, 1, f32 , f32 , f32 , , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_FLOAT , plain, 1, 1, f32 , f32 , f32 , f32 , xyzw, rgb +PIPE_FORMAT_R32_UNORM , plain, 1, 1, un32, , , , x001, rgb +PIPE_FORMAT_R32G32_UNORM , plain, 1, 1, un32, un32, , , xy01, rgb +PIPE_FORMAT_R32G32B32_UNORM , plain, 1, 1, un32, un32, un32, , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_UNORM , plain, 1, 1, un32, un32, un32, un32, xyzw, rgb +PIPE_FORMAT_R32_USCALED , plain, 1, 1, u32 , , , , x001, rgb +PIPE_FORMAT_R32G32_USCALED , plain, 1, 1, u32 , u32 , , , xy01, rgb +PIPE_FORMAT_R32G32B32_USCALED , plain, 1, 1, u32 , u32 , u32 , , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_USCALED , plain, 1, 1, u32 , u32 , u32 , u32 , xyzw, rgb +PIPE_FORMAT_R32_SNORM , plain, 1, 1, sn32, , , , x001, rgb +PIPE_FORMAT_R32G32_SNORM , plain, 1, 1, sn32, sn32, , , xy01, rgb +PIPE_FORMAT_R32G32B32_SNORM , plain, 1, 1, sn32, sn32, sn32, , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_SNORM , plain, 1, 1, sn32, sn32, sn32, sn32, xyzw, rgb +PIPE_FORMAT_R32_SSCALED , plain, 1, 1, s32 , , , , x001, rgb +PIPE_FORMAT_R32G32_SSCALED , plain, 1, 1, s32 , s32 , , , xy01, rgb +PIPE_FORMAT_R32G32B32_SSCALED , plain, 1, 1, s32 , s32 , s32 , , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_SSCALED , plain, 1, 1, s32 , s32 , s32 , s32 , xyzw, rgb +PIPE_FORMAT_R16_FLOAT , plain, 1, 1, f16 , , , , x001, rgb +PIPE_FORMAT_R16G16_FLOAT , plain, 1, 1, f16 , f16 , , , xy01, rgb +PIPE_FORMAT_R16G16B16_FLOAT , plain, 1, 1, f16 , f16 , f16 , , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_FLOAT , plain, 1, 1, f16 , f16 , f16 , f16 , xyzw, rgb +PIPE_FORMAT_R16_UNORM , plain, 1, 1, un16, , , , x001, rgb +PIPE_FORMAT_R16G16_UNORM , plain, 1, 1, un16, un16, , , xy01, rgb +PIPE_FORMAT_R16G16B16_UNORM , plain, 1, 1, un16, un16, un16, , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_UNORM , plain, 1, 1, un16, un16, un16, un16, xyzw, rgb +PIPE_FORMAT_R16_USCALED , plain, 1, 1, u16 , , , , x001, rgb +PIPE_FORMAT_R16G16_USCALED , plain, 1, 1, u16 , u16 , , , xy01, rgb +PIPE_FORMAT_R16G16B16_USCALED , plain, 1, 1, u16 , u16 , u16 , , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_USCALED , plain, 1, 1, u16 , u16 , u16 , u16 , xyzw, rgb +PIPE_FORMAT_R16_SNORM , plain, 1, 1, sn16, , , , x001, rgb +PIPE_FORMAT_R16G16_SNORM , plain, 1, 1, sn16, sn16, , , xy01, rgb +PIPE_FORMAT_R16G16B16_SNORM , plain, 1, 1, sn16, sn16, sn16, , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_SNORM , plain, 1, 1, sn16, sn16, sn16, sn16, xyzw, rgb +PIPE_FORMAT_R16_SSCALED , plain, 1, 1, s16 , , , , x001, rgb +PIPE_FORMAT_R16G16_SSCALED , plain, 1, 1, s16 , s16 , , , xy01, rgb +PIPE_FORMAT_R16G16B16_SSCALED , plain, 1, 1, s16 , s16 , s16 , , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_SSCALED , plain, 1, 1, s16 , s16 , s16 , s16 , xyzw, rgb +PIPE_FORMAT_R8_UNORM , plain, 1, 1, un8 , , , , x001, rgb +PIPE_FORMAT_R8G8_UNORM , plain, 1, 1, un8 , un8 , , , xy01, rgb +PIPE_FORMAT_R8G8B8_UNORM , plain, 1, 1, un8 , un8 , un8 , , xyz1, rgb +PIPE_FORMAT_R8G8B8A8_UNORM , plain, 1, 1, un8 , un8 , un8 , un8 , xyzw, rgb +PIPE_FORMAT_R8_USCALED , plain, 1, 1, u8 , , , , x001, rgb +PIPE_FORMAT_R8G8_USCALED , plain, 1, 1, u8 , u8 , , , xy01, rgb +PIPE_FORMAT_R8G8B8_USCALED , plain, 1, 1, u8 , u8 , u8 , , xyz1, rgb +PIPE_FORMAT_R8G8B8A8_USCALED , plain, 1, 1, u8 , u8 , u8 , u8 , xyzw, rgb +PIPE_FORMAT_R8_SNORM , plain, 1, 1, sn8 , , , , x001, rgb +PIPE_FORMAT_R8G8_SNORM , plain, 1, 1, sn8 , sn8 , , , xy01, rgb +PIPE_FORMAT_R8G8B8_SNORM , plain, 1, 1, sn8 , sn8 , sn8 , , xyz1, rgb +PIPE_FORMAT_R8G8B8A8_SNORM , plain, 1, 1, sn8 , sn8 , sn8 , sn8 , xyzw, rgb +PIPE_FORMAT_R8_SSCALED , plain, 1, 1, s8 , , , , x001, rgb +PIPE_FORMAT_R8G8_SSCALED , plain, 1, 1, s8 , s8 , , , xy01, rgb +PIPE_FORMAT_R8G8B8_SSCALED , plain, 1, 1, s8 , s8 , s8 , , xyz1, rgb +PIPE_FORMAT_R8G8B8A8_SSCALED , plain, 1, 1, s8 , s8 , s8 , s8 , xyzw, rgb + +# GL-specific vertex buffer element formats +# A.k.a. GL_FIXED +PIPE_FORMAT_R32_FIXED , plain, 1, 1, h32 , , , , x001, rgb +PIPE_FORMAT_R32G32_FIXED , plain, 1, 1, h32 , h32 , , , xy01, rgb +PIPE_FORMAT_R32G32B32_FIXED , plain, 1, 1, h32 , h32 , h32 , , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_FIXED , plain, 1, 1, h32 , h32 , h32 , h32 , xyzw, rgb + +# D3D9-specific vertex buffer element formats +# See also: +# - http://msdn.microsoft.com/en-us/library/bb172533.aspx +# A.k.a. D3DDECLTYPE_UDEC3 +PIPE_FORMAT_R10G10B10X2_USCALED , plain, 1, 1, u10 , u10 , u10 , x2 , xyz1, rgb, x2 , u10 , u10 , u10 , wzy1 +# A.k.a. D3DDECLTYPE_DEC3N +PIPE_FORMAT_R10G10B10X2_SNORM , plain, 1, 1, sn10, sn10, sn10 , x2 , xyz1, rgb, x2 , sn10, sn10, sn10, wzy1 + +PIPE_FORMAT_YV12 , other, 1, 1, x8 , x8 , x8 , x8 , xyzw, yuv +PIPE_FORMAT_YV16 , other, 1, 1, x8 , x8 , x8 , x8 , xyzw, yuv +PIPE_FORMAT_IYUV , other, 1, 1, x8 , x8 , x8 , x8 , xyzw, yuv +PIPE_FORMAT_NV12 , other, 1, 1, x8 , x8 , x8 , x8 , xyzw, yuv +PIPE_FORMAT_NV21 , other, 1, 1, x8 , x8 , x8 , x8 , xyzw, yuv + +# Usually used to implement IA44 and AI44 formats in video decoding +PIPE_FORMAT_A4R4_UNORM , plain, 1, 1, un4 , un4 , , , y00x, rgb, un4, un4 , , , x00y +PIPE_FORMAT_R4A4_UNORM , plain, 1, 1, un4 , un4 , , , x00y, rgb, un4, un4 , , , y00x +PIPE_FORMAT_R8A8_UNORM , plain, 1, 1, un8 , un8 , , , x00y, rgb +PIPE_FORMAT_A8R8_UNORM , plain, 1, 1, un8 , un8 , , , y00x, rgb + +# ARB_vertex_type_10_10_10_2_REV +PIPE_FORMAT_R10G10B10A2_USCALED , plain, 1, 1, u10 , u10 , u10 , u2 , xyzw, rgb, u2 , u10 , u10 , u10 , wzyx +PIPE_FORMAT_R10G10B10A2_SSCALED , plain, 1, 1, s10 , s10 , s10 , s2 , xyzw, rgb, s2 , s10 , s10 , s10 , wzyx +PIPE_FORMAT_R10G10B10A2_SNORM , plain, 1, 1, sn10, sn10, sn10, sn2 , xyzw, rgb, sn2 , sn10, sn10, sn10, wzyx +PIPE_FORMAT_B10G10R10A2_USCALED , plain, 1, 1, u10 , u10 , u10 , u2 , zyxw, rgb, u2 , u10 , u10 , u10 , yzwx +PIPE_FORMAT_B10G10R10A2_SSCALED , plain, 1, 1, s10 , s10 , s10 , s2 , zyxw, rgb, s2 , s10 , s10 , s10 , yzwx +PIPE_FORMAT_B10G10R10A2_SNORM , plain, 1, 1, sn10, sn10, sn10, sn2 , zyxw, rgb, sn2 , sn10, sn10, sn10, yzwx + +PIPE_FORMAT_R8_UINT , plain, 1, 1, up8, , , , x001, rgb +PIPE_FORMAT_R8G8_UINT , plain, 1, 1, up8, up8, , , xy01, rgb +PIPE_FORMAT_R8G8B8_UINT , plain, 1, 1, up8, up8, up8, , xyz1, rgb +PIPE_FORMAT_R8G8B8A8_UINT , plain, 1, 1, up8, up8, up8, up8, xyzw, rgb + +PIPE_FORMAT_R8_SINT , plain, 1, 1, sp8, , , , x001, rgb +PIPE_FORMAT_R8G8_SINT , plain, 1, 1, sp8, sp8, , , xy01, rgb +PIPE_FORMAT_R8G8B8_SINT , plain, 1, 1, sp8, sp8, sp8, , xyz1, rgb +PIPE_FORMAT_R8G8B8A8_SINT , plain, 1, 1, sp8, sp8, sp8, sp8, xyzw, rgb + +PIPE_FORMAT_R16_UINT , plain, 1, 1, up16, , , , x001, rgb +PIPE_FORMAT_R16G16_UINT , plain, 1, 1, up16, up16, , , xy01, rgb +PIPE_FORMAT_R16G16B16_UINT , plain, 1, 1, up16, up16, up16, , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_UINT , plain, 1, 1, up16, up16, up16, up16, xyzw, rgb + +PIPE_FORMAT_R16_SINT , plain, 1, 1, sp16, , , , x001, rgb +PIPE_FORMAT_R16G16_SINT , plain, 1, 1, sp16, sp16, , , xy01, rgb +PIPE_FORMAT_R16G16B16_SINT , plain, 1, 1, sp16, sp16, sp16, , xyz1, rgb +PIPE_FORMAT_R16G16B16A16_SINT , plain, 1, 1, sp16, sp16, sp16, sp16, xyzw, rgb + +PIPE_FORMAT_R32_UINT , plain, 1, 1, up32, , , , x001, rgb +PIPE_FORMAT_R32G32_UINT , plain, 1, 1, up32, up32, , , xy01, rgb +PIPE_FORMAT_R32G32B32_UINT , plain, 1, 1, up32, up32, up32, , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_UINT , plain, 1, 1, up32, up32, up32, up32, xyzw, rgb + +PIPE_FORMAT_R32_SINT , plain, 1, 1, sp32, , , , x001, rgb +PIPE_FORMAT_R32G32_SINT , plain, 1, 1, sp32, sp32, , , xy01, rgb +PIPE_FORMAT_R32G32B32_SINT , plain, 1, 1, sp32, sp32, sp32, , xyz1, rgb +PIPE_FORMAT_R32G32B32A32_SINT , plain, 1, 1, sp32, sp32, sp32, sp32, xyzw, rgb + +PIPE_FORMAT_A8_UINT , plain, 1, 1, up8, , , , 000x, rgb +PIPE_FORMAT_I8_UINT , plain, 1, 1, up8, , , , xxxx, rgb +PIPE_FORMAT_L8_UINT , plain, 1, 1, up8, , , , xxx1, rgb +PIPE_FORMAT_L8A8_UINT , plain, 1, 1, up8, up8, , , xxxy, rgb + +PIPE_FORMAT_A8_SINT , plain, 1, 1, sp8, , , , 000x, rgb +PIPE_FORMAT_I8_SINT , plain, 1, 1, sp8, , , , xxxx, rgb +PIPE_FORMAT_L8_SINT , plain, 1, 1, sp8, , , , xxx1, rgb +PIPE_FORMAT_L8A8_SINT , plain, 1, 1, sp8, sp8, , , xxxy, rgb + +PIPE_FORMAT_A16_UINT , plain, 1, 1, up16, , , , 000x, rgb +PIPE_FORMAT_I16_UINT , plain, 1, 1, up16, , , , xxxx, rgb +PIPE_FORMAT_L16_UINT , plain, 1, 1, up16, , , , xxx1, rgb +PIPE_FORMAT_L16A16_UINT , plain, 1, 1, up16, up16, , , xxxy, rgb + +PIPE_FORMAT_A16_SINT , plain, 1, 1, sp16, , , , 000x, rgb +PIPE_FORMAT_I16_SINT , plain, 1, 1, sp16, , , , xxxx, rgb +PIPE_FORMAT_L16_SINT , plain, 1, 1, sp16, , , , xxx1, rgb +PIPE_FORMAT_L16A16_SINT , plain, 1, 1, sp16, sp16, , , xxxy, rgb + +PIPE_FORMAT_A32_UINT , plain, 1, 1, up32, , , , 000x, rgb +PIPE_FORMAT_I32_UINT , plain, 1, 1, up32, , , , xxxx, rgb +PIPE_FORMAT_L32_UINT , plain, 1, 1, up32, , , , xxx1, rgb +PIPE_FORMAT_L32A32_UINT , plain, 1, 1, up32, up32, , , xxxy, rgb + +PIPE_FORMAT_A32_SINT , plain, 1, 1, sp32, , , , 000x, rgb +PIPE_FORMAT_I32_SINT , plain, 1, 1, sp32, , , , xxxx, rgb +PIPE_FORMAT_L32_SINT , plain, 1, 1, sp32, , , , xxx1, rgb +PIPE_FORMAT_L32A32_SINT , plain, 1, 1, sp32, sp32, , , xxxy, rgb + +PIPE_FORMAT_B10G10R10A2_UINT , plain, 1, 1, up10, up10, up10, up2, zyxw, rgb, up2 , up10, up10, up10, yzwx + +PIPE_FORMAT_R8G8B8X8_SNORM , plain, 1, 1, sn8, sn8, sn8, x8, xyz1, rgb +PIPE_FORMAT_R8G8B8X8_SRGB , plain, 1, 1, un8, un8, un8, x8, xyz1, srgb +PIPE_FORMAT_R8G8B8X8_UINT , plain, 1, 1, up8, up8, up8, x8, xyz1, rgb +PIPE_FORMAT_R8G8B8X8_SINT , plain, 1, 1, sp8, sp8, sp8, x8, xyz1, rgb +PIPE_FORMAT_B10G10R10X2_UNORM , plain, 1, 1, un10, un10, un10, x2, zyx1, rgb, x2 , un10, un10, un10, yzw1 +PIPE_FORMAT_R16G16B16X16_UNORM , plain, 1, 1, un16, un16, un16, x16, xyz1, rgb +PIPE_FORMAT_R16G16B16X16_SNORM , plain, 1, 1, sn16, sn16, sn16, x16, xyz1, rgb +PIPE_FORMAT_R16G16B16X16_FLOAT , plain, 1, 1, f16, f16, f16, x16, xyz1, rgb +PIPE_FORMAT_R16G16B16X16_UINT , plain, 1, 1, up16, up16, up16, x16, xyz1, rgb +PIPE_FORMAT_R16G16B16X16_SINT , plain, 1, 1, sp16, sp16, sp16, x16, xyz1, rgb +PIPE_FORMAT_R32G32B32X32_FLOAT , plain, 1, 1, f32, f32, f32, x32, xyz1, rgb +PIPE_FORMAT_R32G32B32X32_UINT , plain, 1, 1, up32, up32, up32, x32, xyz1, rgb +PIPE_FORMAT_R32G32B32X32_SINT , plain, 1, 1, sp32, sp32, sp32, x32, xyz1, rgb + +PIPE_FORMAT_R8A8_SNORM , plain, 1, 1, sn8 , sn8 , , , x00y, rgb +PIPE_FORMAT_R16A16_UNORM , plain, 1, 1, un16 , un16 , , , x00y, rgb +PIPE_FORMAT_R16A16_SNORM , plain, 1, 1, sn16 , sn16 , , , x00y, rgb +PIPE_FORMAT_R16A16_FLOAT , plain, 1, 1, f16 , f16 , , , x00y, rgb +PIPE_FORMAT_R32A32_FLOAT , plain, 1, 1, f32 , f32 , , , x00y, rgb +PIPE_FORMAT_R8A8_UINT , plain, 1, 1, up8 , up8 , , , x00y, rgb +PIPE_FORMAT_R8A8_SINT , plain, 1, 1, sp8 , sp8 , , , x00y, rgb +PIPE_FORMAT_R16A16_UINT , plain, 1, 1, up16 , up16 , , , x00y, rgb +PIPE_FORMAT_R16A16_SINT , plain, 1, 1, sp16 , sp16 , , , x00y, rgb +PIPE_FORMAT_R32A32_UINT , plain, 1, 1, up32 , up32 , , , x00y, rgb +PIPE_FORMAT_R32A32_SINT , plain, 1, 1, sp32 , sp32 , , , x00y, rgb +PIPE_FORMAT_R10G10B10A2_UINT , plain, 1, 1, up10 , up10 , up10, up2 , xyzw, rgb, up2 , up10, up10, up10, wzyx + +PIPE_FORMAT_B5G6R5_SRGB , plain, 1, 1, un5 , un6 , un5 , , zyx1, srgb, un5 , un6 , un5 , , xyz1 + +PIPE_FORMAT_A8L8_UNORM , plain, 1, 1, un8 , un8 , , , yyyx, rgb +PIPE_FORMAT_A8L8_SNORM , plain, 1, 1, sn8 , sn8 , , , yyyx, rgb +PIPE_FORMAT_A8L8_SRGB , plain, 1, 1, un8 , un8 , , , yyyx, srgb +PIPE_FORMAT_A16L16_UNORM , plain, 1, 1, un16, un16, , , yyyx, rgb + +PIPE_FORMAT_G8R8_UNORM , plain, 1, 1, un8 , un8 , , , yx01, rgb +PIPE_FORMAT_G8R8_SNORM , plain, 1, 1, sn8 , sn8 , , , yx01, rgb +PIPE_FORMAT_G16R16_UNORM , plain, 1, 1, un16, un16, , , yx01, rgb +PIPE_FORMAT_G16R16_SNORM , plain, 1, 1, sn16, sn16, , , yx01, rgb + +PIPE_FORMAT_A8B8G8R8_SNORM , plain, 1, 1, sn8 , sn8 , sn8 , sn8 , wzyx, rgb +PIPE_FORMAT_X8B8G8R8_SNORM , plain, 1, 1, x8, sn8, sn8, sn8, wzy1, rgb diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.h new file mode 100644 index 0000000..1990c60 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format.h @@ -0,0 +1,1009 @@ +/************************************************************************** + * + * Copyright 2009-2010 Vmware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef U_FORMAT_H +#define U_FORMAT_H + + +#include "pipe/p_format.h" +#include "pipe/p_defines.h" +#include "util/u_debug.h" + +union pipe_color_union; + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Describe how to pack/unpack pixels into/from the prescribed format. + * + * XXX: This could be renamed to something like util_format_pack, or broke down + * in flags inside util_format_block that said exactly what we want. + */ +enum util_format_layout { + /** + * Formats with util_format_block::width == util_format_block::height == 1 + * that can be described as an ordinary data structure. + */ + UTIL_FORMAT_LAYOUT_PLAIN = 0, + + /** + * Formats with sub-sampled channels. + * + * This is for formats like YVYU where there is less than one sample per + * pixel. + */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED = 3, + + /** + * S3 Texture Compression formats. + */ + UTIL_FORMAT_LAYOUT_S3TC = 4, + + /** + * Red-Green Texture Compression formats. + */ + UTIL_FORMAT_LAYOUT_RGTC = 5, + + /** + * Ericsson Texture Compression + */ + UTIL_FORMAT_LAYOUT_ETC = 6, + + /** + * BC6/7 Texture Compression + */ + UTIL_FORMAT_LAYOUT_BPTC = 7, + + /** + * Everything else that doesn't fit in any of the above layouts. + */ + UTIL_FORMAT_LAYOUT_OTHER = 8 +}; + + +struct util_format_block +{ + /** Block width in pixels */ + unsigned width; + + /** Block height in pixels */ + unsigned height; + + /** Block size in bits */ + unsigned bits; +}; + + +enum util_format_type { + UTIL_FORMAT_TYPE_VOID = 0, + UTIL_FORMAT_TYPE_UNSIGNED = 1, + UTIL_FORMAT_TYPE_SIGNED = 2, + UTIL_FORMAT_TYPE_FIXED = 3, + UTIL_FORMAT_TYPE_FLOAT = 4 +}; + + +enum util_format_swizzle { + UTIL_FORMAT_SWIZZLE_X = 0, + UTIL_FORMAT_SWIZZLE_Y = 1, + UTIL_FORMAT_SWIZZLE_Z = 2, + UTIL_FORMAT_SWIZZLE_W = 3, + UTIL_FORMAT_SWIZZLE_0 = 4, + UTIL_FORMAT_SWIZZLE_1 = 5, + UTIL_FORMAT_SWIZZLE_NONE = 6, + UTIL_FORMAT_SWIZZLE_MAX = 7 /**< Number of enums counter (must be last) */ +}; + + +enum util_format_colorspace { + UTIL_FORMAT_COLORSPACE_RGB = 0, + UTIL_FORMAT_COLORSPACE_SRGB = 1, + UTIL_FORMAT_COLORSPACE_YUV = 2, + UTIL_FORMAT_COLORSPACE_ZS = 3 +}; + + +struct util_format_channel_description +{ + unsigned type:5; /**< UTIL_FORMAT_TYPE_x */ + unsigned normalized:1; + unsigned pure_integer:1; + unsigned size:9; /**< bits per channel */ + unsigned shift:16; /** number of bits from lsb */ +}; + + +struct util_format_description +{ + enum pipe_format format; + + const char *name; + + /** + * Short name, striped of the prefix, lower case. + */ + const char *short_name; + + /** + * Pixel block dimensions. + */ + struct util_format_block block; + + enum util_format_layout layout; + + /** + * The number of channels. + */ + unsigned nr_channels:3; + + /** + * Whether all channels have the same number of (whole) bytes and type. + */ + unsigned is_array:1; + + /** + * Whether the pixel format can be described as a bitfield structure. + * + * In particular: + * - pixel depth must be 8, 16, or 32 bits; + * - all channels must be unsigned, signed, or void + */ + unsigned is_bitmask:1; + + /** + * Whether channels have mixed types (ignoring UTIL_FORMAT_TYPE_VOID). + */ + unsigned is_mixed:1; + + /** + * Input channel description, in the order XYZW. + * + * Only valid for UTIL_FORMAT_LAYOUT_PLAIN formats. + * + * If each channel is accessed as an individual N-byte value, X is always + * at the lowest address in memory, Y is always next, and so on. For all + * currently-defined formats, the N-byte value has native endianness. + * + * If instead a group of channels is accessed as a single N-byte value, + * the order of the channels within that value depends on endianness. + * For big-endian targets, X is the most significant subvalue, + * otherwise it is the least significant one. + * + * For example, if X is 8 bits and Y is 24 bits, the memory order is: + * + * 0 1 2 3 + * little-endian: X Yl Ym Yu (l = lower, m = middle, u = upper) + * big-endian: X Yu Ym Yl + * + * If X is 5 bits, Y is 5 bits, Z is 5 bits and W is 1 bit, the layout is: + * + * 0 1 + * msb lsb msb lsb + * little-endian: YYYXXXXX WZZZZZYY + * big-endian: XXXXXYYY YYZZZZZW + */ + struct util_format_channel_description channel[4]; + + /** + * Output channel swizzle. + * + * The order is either: + * - RGBA + * - YUV(A) + * - ZS + * depending on the colorspace. + */ + unsigned char swizzle[4]; + + /** + * Colorspace transformation. + */ + enum util_format_colorspace colorspace; +}; + + +extern const struct util_format_description +util_format_description_table[]; + + +const struct util_format_description * +util_format_description(enum pipe_format format); + + +/* + * Format query functions. + */ + +static inline const char * +util_format_name(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return "PIPE_FORMAT_???"; + } + + return desc->name; +} + +static inline const char * +util_format_short_name(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return "???"; + } + + return desc->short_name; +} + +/** + * Whether this format is plain, see UTIL_FORMAT_LAYOUT_PLAIN for more info. + */ +static inline boolean +util_format_is_plain(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + if (!format) { + return FALSE; + } + + return desc->layout == UTIL_FORMAT_LAYOUT_PLAIN ? TRUE : FALSE; +} + +static inline boolean +util_format_is_compressed(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return FALSE; + } + + switch (desc->layout) { + case UTIL_FORMAT_LAYOUT_S3TC: + case UTIL_FORMAT_LAYOUT_RGTC: + case UTIL_FORMAT_LAYOUT_ETC: + case UTIL_FORMAT_LAYOUT_BPTC: + /* XXX add other formats in the future */ + return TRUE; + default: + return FALSE; + } +} + +static inline boolean +util_format_is_s3tc(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return FALSE; + } + + return desc->layout == UTIL_FORMAT_LAYOUT_S3TC ? TRUE : FALSE; +} + +static inline boolean +util_format_is_srgb(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + return desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB; +} + +static inline boolean +util_format_has_depth(const struct util_format_description *desc) +{ + return desc->colorspace == UTIL_FORMAT_COLORSPACE_ZS && + desc->swizzle[0] != UTIL_FORMAT_SWIZZLE_NONE; +} + +static inline boolean +util_format_has_stencil(const struct util_format_description *desc) +{ + return desc->colorspace == UTIL_FORMAT_COLORSPACE_ZS && + desc->swizzle[1] != UTIL_FORMAT_SWIZZLE_NONE; +} + +static inline boolean +util_format_is_depth_or_stencil(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return FALSE; + } + + return util_format_has_depth(desc) || + util_format_has_stencil(desc); +} + +static inline boolean +util_format_is_depth_and_stencil(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return FALSE; + } + + return util_format_has_depth(desc) && + util_format_has_stencil(desc); +} + + +/** + * Calculates the depth format type based upon the incoming format description. + */ +static inline unsigned +util_get_depth_format_type(const struct util_format_description *desc) +{ + unsigned depth_channel = desc->swizzle[0]; + if (desc->colorspace == UTIL_FORMAT_COLORSPACE_ZS && + depth_channel != UTIL_FORMAT_SWIZZLE_NONE) { + return desc->channel[depth_channel].type; + } else { + return UTIL_FORMAT_TYPE_VOID; + } +} + + +/** + * Calculates the MRD for the depth format. MRD is used in depth bias + * for UNORM and unbound depth buffers. When the depth buffer is floating + * point, the depth bias calculation does not use the MRD. However, the + * default MRD will be 1.0 / ((1 << 24) - 1). + */ +double +util_get_depth_format_mrd(const struct util_format_description *desc); + + +/** + * Return whether this is an RGBA, Z, S, or combined ZS format. + * Useful for initializing pipe_blit_info::mask. + */ +static inline unsigned +util_format_get_mask(enum pipe_format format) +{ + const struct util_format_description *desc = + util_format_description(format); + + if (!desc) + return 0; + + if (util_format_has_depth(desc)) { + if (util_format_has_stencil(desc)) { + return PIPE_MASK_ZS; + } else { + return PIPE_MASK_Z; + } + } else { + if (util_format_has_stencil(desc)) { + return PIPE_MASK_S; + } else { + return PIPE_MASK_RGBA; + } + } +} + +/** + * Give the RGBA colormask of the channels that can be represented in this + * format. + * + * That is, the channels whose values are preserved. + */ +static inline unsigned +util_format_colormask(const struct util_format_description *desc) +{ + unsigned colormask; + unsigned chan; + + switch (desc->colorspace) { + case UTIL_FORMAT_COLORSPACE_RGB: + case UTIL_FORMAT_COLORSPACE_SRGB: + case UTIL_FORMAT_COLORSPACE_YUV: + colormask = 0; + for (chan = 0; chan < 4; ++chan) { + if (desc->swizzle[chan] < 4) { + colormask |= (1 << chan); + } + } + return colormask; + case UTIL_FORMAT_COLORSPACE_ZS: + return 0; + default: + assert(0); + return 0; + } +} + + +/** + * Checks if color mask covers every channel for the specified format + * + * @param desc a format description to check colormask with + * @param colormask a bit mask for channels, matches format of PIPE_MASK_RGBA + */ +static inline boolean +util_format_colormask_full(const struct util_format_description *desc, unsigned colormask) +{ + return (~colormask & util_format_colormask(desc)) == 0; +} + + +boolean +util_format_is_float(enum pipe_format format); + + +boolean +util_format_has_alpha(enum pipe_format format); + + +boolean +util_format_is_luminance(enum pipe_format format); + +boolean +util_format_is_alpha(enum pipe_format format); + +boolean +util_format_is_luminance_alpha(enum pipe_format format); + + +boolean +util_format_is_intensity(enum pipe_format format); + +boolean +util_format_is_subsampled_422(enum pipe_format format); + +boolean +util_format_is_pure_integer(enum pipe_format format); + +boolean +util_format_is_pure_sint(enum pipe_format format); + +boolean +util_format_is_pure_uint(enum pipe_format format); + +boolean +util_format_is_snorm(enum pipe_format format); + +/** + * Check if the src format can be blitted to the destination format with + * a simple memcpy. For example, blitting from RGBA to RGBx is OK, but not + * the reverse. + */ +boolean +util_is_format_compatible(const struct util_format_description *src_desc, + const struct util_format_description *dst_desc); + +/** + * Whether the format is supported by Gallium for the given bindings. + * This covers S3TC textures and floating-point render targets. + */ +boolean +util_format_is_supported(enum pipe_format format, unsigned bind); + +/** + * Whether this format is a rgab8 variant. + * + * That is, any format that matches the + * + * PIPE_FORMAT_?8?8?8?8_UNORM + */ +static inline boolean +util_format_is_rgba8_variant(const struct util_format_description *desc) +{ + unsigned chan; + + if(desc->block.width != 1 || + desc->block.height != 1 || + desc->block.bits != 32) + return FALSE; + + for(chan = 0; chan < 4; ++chan) { + if(desc->channel[chan].type != UTIL_FORMAT_TYPE_UNSIGNED && + desc->channel[chan].type != UTIL_FORMAT_TYPE_VOID) + return FALSE; + if(desc->channel[chan].type == UTIL_FORMAT_TYPE_UNSIGNED && + !desc->channel[chan].normalized) + return FALSE; + if(desc->channel[chan].size != 8) + return FALSE; + } + + return TRUE; +} + + +/** + * Return total bits needed for the pixel format per block. + */ +static inline uint +util_format_get_blocksizebits(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return 0; + } + + return desc->block.bits; +} + +/** + * Return bytes per block (not pixel) for the given format. + */ +static inline uint +util_format_get_blocksize(enum pipe_format format) +{ + uint bits = util_format_get_blocksizebits(format); + uint bytes = bits / 8; + + assert(bits % 8 == 0); + assert(bytes > 0); + if (bytes == 0) { + bytes = 1; + } + + return bytes; +} + +static inline uint +util_format_get_blockwidth(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return 1; + } + + return desc->block.width; +} + +static inline uint +util_format_get_blockheight(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + + assert(desc); + if (!desc) { + return 1; + } + + return desc->block.height; +} + +static inline unsigned +util_format_get_nblocksx(enum pipe_format format, + unsigned x) +{ + unsigned blockwidth = util_format_get_blockwidth(format); + return (x + blockwidth - 1) / blockwidth; +} + +static inline unsigned +util_format_get_nblocksy(enum pipe_format format, + unsigned y) +{ + unsigned blockheight = util_format_get_blockheight(format); + return (y + blockheight - 1) / blockheight; +} + +static inline unsigned +util_format_get_nblocks(enum pipe_format format, + unsigned width, + unsigned height) +{ + return util_format_get_nblocksx(format, width) * util_format_get_nblocksy(format, height); +} + +static inline size_t +util_format_get_stride(enum pipe_format format, + unsigned width) +{ + return util_format_get_nblocksx(format, width) * util_format_get_blocksize(format); +} + +static inline size_t +util_format_get_2d_size(enum pipe_format format, + size_t stride, + unsigned height) +{ + return util_format_get_nblocksy(format, height) * stride; +} + +static inline uint +util_format_get_component_bits(enum pipe_format format, + enum util_format_colorspace colorspace, + uint component) +{ + const struct util_format_description *desc = util_format_description(format); + enum util_format_colorspace desc_colorspace; + + assert(format); + if (!format) { + return 0; + } + + assert(component < 4); + + /* Treat RGB and SRGB as equivalent. */ + if (colorspace == UTIL_FORMAT_COLORSPACE_SRGB) { + colorspace = UTIL_FORMAT_COLORSPACE_RGB; + } + if (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) { + desc_colorspace = UTIL_FORMAT_COLORSPACE_RGB; + } else { + desc_colorspace = desc->colorspace; + } + + if (desc_colorspace != colorspace) { + return 0; + } + + switch (desc->swizzle[component]) { + case UTIL_FORMAT_SWIZZLE_X: + return desc->channel[0].size; + case UTIL_FORMAT_SWIZZLE_Y: + return desc->channel[1].size; + case UTIL_FORMAT_SWIZZLE_Z: + return desc->channel[2].size; + case UTIL_FORMAT_SWIZZLE_W: + return desc->channel[3].size; + default: + return 0; + } +} + +/** + * Given a linear RGB colorspace format, return the corresponding SRGB + * format, or PIPE_FORMAT_NONE if none. + */ +static inline enum pipe_format +util_format_srgb(enum pipe_format format) +{ + if (util_format_is_srgb(format)) + return format; + + switch (format) { + case PIPE_FORMAT_L8_UNORM: + return PIPE_FORMAT_L8_SRGB; + case PIPE_FORMAT_L8A8_UNORM: + return PIPE_FORMAT_L8A8_SRGB; + case PIPE_FORMAT_R8G8B8_UNORM: + return PIPE_FORMAT_R8G8B8_SRGB; + case PIPE_FORMAT_A8B8G8R8_UNORM: + return PIPE_FORMAT_A8B8G8R8_SRGB; + case PIPE_FORMAT_X8B8G8R8_UNORM: + return PIPE_FORMAT_X8B8G8R8_SRGB; + case PIPE_FORMAT_B8G8R8A8_UNORM: + return PIPE_FORMAT_B8G8R8A8_SRGB; + case PIPE_FORMAT_B8G8R8X8_UNORM: + return PIPE_FORMAT_B8G8R8X8_SRGB; + case PIPE_FORMAT_A8R8G8B8_UNORM: + return PIPE_FORMAT_A8R8G8B8_SRGB; + case PIPE_FORMAT_X8R8G8B8_UNORM: + return PIPE_FORMAT_X8R8G8B8_SRGB; + case PIPE_FORMAT_R8G8B8A8_UNORM: + return PIPE_FORMAT_R8G8B8A8_SRGB; + case PIPE_FORMAT_R8G8B8X8_UNORM: + return PIPE_FORMAT_R8G8B8X8_SRGB; + case PIPE_FORMAT_DXT1_RGB: + return PIPE_FORMAT_DXT1_SRGB; + case PIPE_FORMAT_DXT1_RGBA: + return PIPE_FORMAT_DXT1_SRGBA; + case PIPE_FORMAT_DXT3_RGBA: + return PIPE_FORMAT_DXT3_SRGBA; + case PIPE_FORMAT_DXT5_RGBA: + return PIPE_FORMAT_DXT5_SRGBA; + case PIPE_FORMAT_B5G6R5_UNORM: + return PIPE_FORMAT_B5G6R5_SRGB; + case PIPE_FORMAT_BPTC_RGBA_UNORM: + return PIPE_FORMAT_BPTC_SRGBA; + default: + return PIPE_FORMAT_NONE; + } +} + +/** + * Given an sRGB format, return the corresponding linear colorspace format. + * For non sRGB formats, return the format unchanged. + */ +static inline enum pipe_format +util_format_linear(enum pipe_format format) +{ + switch (format) { + case PIPE_FORMAT_L8_SRGB: + return PIPE_FORMAT_L8_UNORM; + case PIPE_FORMAT_L8A8_SRGB: + return PIPE_FORMAT_L8A8_UNORM; + case PIPE_FORMAT_R8G8B8_SRGB: + return PIPE_FORMAT_R8G8B8_UNORM; + case PIPE_FORMAT_A8B8G8R8_SRGB: + return PIPE_FORMAT_A8B8G8R8_UNORM; + case PIPE_FORMAT_X8B8G8R8_SRGB: + return PIPE_FORMAT_X8B8G8R8_UNORM; + case PIPE_FORMAT_B8G8R8A8_SRGB: + return PIPE_FORMAT_B8G8R8A8_UNORM; + case PIPE_FORMAT_B8G8R8X8_SRGB: + return PIPE_FORMAT_B8G8R8X8_UNORM; + case PIPE_FORMAT_A8R8G8B8_SRGB: + return PIPE_FORMAT_A8R8G8B8_UNORM; + case PIPE_FORMAT_X8R8G8B8_SRGB: + return PIPE_FORMAT_X8R8G8B8_UNORM; + case PIPE_FORMAT_R8G8B8A8_SRGB: + return PIPE_FORMAT_R8G8B8A8_UNORM; + case PIPE_FORMAT_R8G8B8X8_SRGB: + return PIPE_FORMAT_R8G8B8X8_UNORM; + case PIPE_FORMAT_DXT1_SRGB: + return PIPE_FORMAT_DXT1_RGB; + case PIPE_FORMAT_DXT1_SRGBA: + return PIPE_FORMAT_DXT1_RGBA; + case PIPE_FORMAT_DXT3_SRGBA: + return PIPE_FORMAT_DXT3_RGBA; + case PIPE_FORMAT_DXT5_SRGBA: + return PIPE_FORMAT_DXT5_RGBA; + case PIPE_FORMAT_B5G6R5_SRGB: + return PIPE_FORMAT_B5G6R5_UNORM; + case PIPE_FORMAT_BPTC_SRGBA: + return PIPE_FORMAT_BPTC_RGBA_UNORM; + default: + return format; + } +} + +/** + * Given a depth-stencil format, return the corresponding stencil-only format. + * For stencil-only formats, return the format unchanged. + */ +static inline enum pipe_format +util_format_stencil_only(enum pipe_format format) +{ + switch (format) { + /* mask out the depth component */ + case PIPE_FORMAT_Z24_UNORM_S8_UINT: + return PIPE_FORMAT_X24S8_UINT; + case PIPE_FORMAT_S8_UINT_Z24_UNORM: + return PIPE_FORMAT_S8X24_UINT; + case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: + return PIPE_FORMAT_X32_S8X24_UINT; + + /* stencil only formats */ + case PIPE_FORMAT_X24S8_UINT: + case PIPE_FORMAT_S8X24_UINT: + case PIPE_FORMAT_X32_S8X24_UINT: + case PIPE_FORMAT_S8_UINT: + return format; + + default: + assert(0); + return PIPE_FORMAT_NONE; + } +} + +/** + * Converts PIPE_FORMAT_*I* to PIPE_FORMAT_*R*. + * This is identity for non-intensity formats. + */ +static inline enum pipe_format +util_format_intensity_to_red(enum pipe_format format) +{ + switch (format) { + case PIPE_FORMAT_I8_UNORM: + return PIPE_FORMAT_R8_UNORM; + case PIPE_FORMAT_I8_SNORM: + return PIPE_FORMAT_R8_SNORM; + case PIPE_FORMAT_I16_UNORM: + return PIPE_FORMAT_R16_UNORM; + case PIPE_FORMAT_I16_SNORM: + return PIPE_FORMAT_R16_SNORM; + case PIPE_FORMAT_I16_FLOAT: + return PIPE_FORMAT_R16_FLOAT; + case PIPE_FORMAT_I32_FLOAT: + return PIPE_FORMAT_R32_FLOAT; + case PIPE_FORMAT_I8_UINT: + return PIPE_FORMAT_R8_UINT; + case PIPE_FORMAT_I8_SINT: + return PIPE_FORMAT_R8_SINT; + case PIPE_FORMAT_I16_UINT: + return PIPE_FORMAT_R16_UINT; + case PIPE_FORMAT_I16_SINT: + return PIPE_FORMAT_R16_SINT; + case PIPE_FORMAT_I32_UINT: + return PIPE_FORMAT_R32_UINT; + case PIPE_FORMAT_I32_SINT: + return PIPE_FORMAT_R32_SINT; + default: + assert(!util_format_is_intensity(format)); + return format; + } +} + +/** + * Converts PIPE_FORMAT_*L* to PIPE_FORMAT_*R*. + * This is identity for non-luminance formats. + */ +static inline enum pipe_format +util_format_luminance_to_red(enum pipe_format format) +{ + switch (format) { + case PIPE_FORMAT_L8_UNORM: + return PIPE_FORMAT_R8_UNORM; + case PIPE_FORMAT_L8_SNORM: + return PIPE_FORMAT_R8_SNORM; + case PIPE_FORMAT_L16_UNORM: + return PIPE_FORMAT_R16_UNORM; + case PIPE_FORMAT_L16_SNORM: + return PIPE_FORMAT_R16_SNORM; + case PIPE_FORMAT_L16_FLOAT: + return PIPE_FORMAT_R16_FLOAT; + case PIPE_FORMAT_L32_FLOAT: + return PIPE_FORMAT_R32_FLOAT; + case PIPE_FORMAT_L8_UINT: + return PIPE_FORMAT_R8_UINT; + case PIPE_FORMAT_L8_SINT: + return PIPE_FORMAT_R8_SINT; + case PIPE_FORMAT_L16_UINT: + return PIPE_FORMAT_R16_UINT; + case PIPE_FORMAT_L16_SINT: + return PIPE_FORMAT_R16_SINT; + case PIPE_FORMAT_L32_UINT: + return PIPE_FORMAT_R32_UINT; + case PIPE_FORMAT_L32_SINT: + return PIPE_FORMAT_R32_SINT; + + case PIPE_FORMAT_LATC1_UNORM: + return PIPE_FORMAT_RGTC1_UNORM; + case PIPE_FORMAT_LATC1_SNORM: + return PIPE_FORMAT_RGTC1_SNORM; + + case PIPE_FORMAT_L4A4_UNORM: + return PIPE_FORMAT_R4A4_UNORM; + + case PIPE_FORMAT_L8A8_UNORM: + return PIPE_FORMAT_R8A8_UNORM; + case PIPE_FORMAT_L8A8_SNORM: + return PIPE_FORMAT_R8A8_SNORM; + case PIPE_FORMAT_L16A16_UNORM: + return PIPE_FORMAT_R16A16_UNORM; + case PIPE_FORMAT_L16A16_SNORM: + return PIPE_FORMAT_R16A16_SNORM; + case PIPE_FORMAT_L16A16_FLOAT: + return PIPE_FORMAT_R16A16_FLOAT; + case PIPE_FORMAT_L32A32_FLOAT: + return PIPE_FORMAT_R32A32_FLOAT; + case PIPE_FORMAT_L8A8_UINT: + return PIPE_FORMAT_R8A8_UINT; + case PIPE_FORMAT_L8A8_SINT: + return PIPE_FORMAT_R8A8_SINT; + case PIPE_FORMAT_L16A16_UINT: + return PIPE_FORMAT_R16A16_UINT; + case PIPE_FORMAT_L16A16_SINT: + return PIPE_FORMAT_R16A16_SINT; + case PIPE_FORMAT_L32A32_UINT: + return PIPE_FORMAT_R32A32_UINT; + case PIPE_FORMAT_L32A32_SINT: + return PIPE_FORMAT_R32A32_SINT; + + /* We don't have compressed red-alpha variants for these. */ + case PIPE_FORMAT_LATC2_UNORM: + case PIPE_FORMAT_LATC2_SNORM: + return PIPE_FORMAT_NONE; + + default: + assert(!util_format_is_luminance(format) && + !util_format_is_luminance_alpha(format)); + return format; + } +} + +/** + * Return the number of components stored. + * Formats with block size != 1x1 will always have 1 component (the block). + */ +static inline unsigned +util_format_get_nr_components(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + return desc->nr_channels; +} + +/** + * Return the index of the first non-void channel + * -1 if no non-void channels + */ +static inline int +util_format_get_first_non_void_channel(enum pipe_format format) +{ + const struct util_format_description *desc = util_format_description(format); + int i; + + for (i = 0; i < 4; i++) + if (desc->channel[i].type != UTIL_FORMAT_TYPE_VOID) + break; + + if (i == 4) + return -1; + + return i; +} + +/* + * Generic format conversion; + */ + +boolean +util_format_fits_8unorm(const struct util_format_description *format_desc); + +/* + * Swizzle operations. + */ + +/* Compose two sets of swizzles. + * If V is a 4D vector and the function parameters represent functions that + * swizzle vector components, this holds: + * swz2(swz1(V)) = dst(V) + */ +void util_format_compose_swizzles(const unsigned char swz1[4], + const unsigned char swz2[4], + unsigned char dst[4]); + +/* Apply the swizzle provided in \param swz (which is one of PIPE_SWIZZLE_x) + * to \param src and store the result in \param dst. + * \param is_integer determines the value written for PIPE_SWIZZLE_ONE. + */ +void util_format_apply_color_swizzle(union pipe_color_union *dst, + const union pipe_color_union *src, + const unsigned char swz[4], + const boolean is_integer); + +void util_format_swizzle_4f(float *dst, const float *src, + const unsigned char swz[4]); + +void util_format_unswizzle_4f(float *dst, const float *src, + const unsigned char swz[4]); + +#ifdef __cplusplus +} // extern "C" { +#endif + +#endif /* ! U_FORMAT_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_parse.py b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_parse.py new file mode 100644 index 0000000..48cc012 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_parse.py @@ -0,0 +1,373 @@ + +''' +/************************************************************************** + * + * Copyright 2009 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +''' + + +from __future__ import division + + +VOID, UNSIGNED, SIGNED, FIXED, FLOAT = range(5) + +SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W, SWIZZLE_0, SWIZZLE_1, SWIZZLE_NONE, = range(7) + +PLAIN = 'plain' + +RGB = 'rgb' +SRGB = 'srgb' +YUV = 'yuv' +ZS = 'zs' + + +def is_pot(x): + return (x & (x - 1)) == 0 + + +VERY_LARGE = 99999999999999999999999 + + +class Channel: + '''Describe the channel of a color channel.''' + + def __init__(self, type, norm, pure, size, name = ''): + self.type = type + self.norm = norm + self.pure = pure + self.size = size + self.sign = type in (SIGNED, FIXED, FLOAT) + self.name = name + + def __str__(self): + s = str(self.type) + if self.norm: + s += 'n' + if self.pure: + s += 'p' + s += str(self.size) + return s + + def __eq__(self, other): + if other is None: + return False + + return self.type == other.type and self.norm == other.norm and self.pure == other.pure and self.size == other.size + + def __ne__(self, other): + return not self == other + + def max(self): + '''Maximum representable number.''' + if self.type == FLOAT: + return VERY_LARGE + if self.type == FIXED: + return (1 << (self.size // 2)) - 1 + if self.norm: + return 1 + if self.type == UNSIGNED: + return (1 << self.size) - 1 + if self.type == SIGNED: + return (1 << (self.size - 1)) - 1 + assert False + + def min(self): + '''Minimum representable number.''' + if self.type == FLOAT: + return -VERY_LARGE + if self.type == FIXED: + return -(1 << (self.size // 2)) + if self.type == UNSIGNED: + return 0 + if self.norm: + return -1 + if self.type == SIGNED: + return -(1 << (self.size - 1)) + assert False + + +class Format: + '''Describe a pixel format.''' + + def __init__(self, name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace): + self.name = name + self.layout = layout + self.block_width = block_width + self.block_height = block_height + self.le_channels = le_channels + self.le_swizzles = le_swizzles + self.be_channels = be_channels + self.be_swizzles = be_swizzles + self.name = name + self.colorspace = colorspace + + def __str__(self): + return self.name + + def short_name(self): + '''Make up a short norm for a format, suitable to be used as suffix in + function names.''' + + name = self.name + if name.startswith('PIPE_FORMAT_'): + name = name[len('PIPE_FORMAT_'):] + name = name.lower() + return name + + def block_size(self): + size = 0 + for channel in self.le_channels: + size += channel.size + return size + + def nr_channels(self): + nr_channels = 0 + for channel in self.le_channels: + if channel.size: + nr_channels += 1 + return nr_channels + + def array_element(self): + if self.layout != PLAIN: + return None + ref_channel = self.le_channels[0] + if ref_channel.type == VOID: + ref_channel = self.le_channels[1] + for channel in self.le_channels: + if channel.size and (channel.size != ref_channel.size or channel.size % 8): + return None + if channel.type != VOID: + if channel.type != ref_channel.type: + return None + if channel.norm != ref_channel.norm: + return None + if channel.pure != ref_channel.pure: + return None + return ref_channel + + def is_array(self): + return self.array_element() != None + + def is_mixed(self): + if self.layout != PLAIN: + return False + ref_channel = self.le_channels[0] + if ref_channel.type == VOID: + ref_channel = self.le_channels[1] + for channel in self.le_channels[1:]: + if channel.type != VOID: + if channel.type != ref_channel.type: + return True + if channel.norm != ref_channel.norm: + return True + if channel.pure != ref_channel.pure: + return True + return False + + def is_pot(self): + return is_pot(self.block_size()) + + def is_int(self): + if self.layout != PLAIN: + return False + for channel in self.le_channels: + if channel.type not in (VOID, UNSIGNED, SIGNED): + return False + return True + + def is_float(self): + if self.layout != PLAIN: + return False + for channel in self.le_channels: + if channel.type not in (VOID, FLOAT): + return False + return True + + def is_bitmask(self): + if self.layout != PLAIN: + return False + if self.block_size() not in (8, 16, 32): + return False + for channel in self.le_channels: + if channel.type not in (VOID, UNSIGNED, SIGNED): + return False + return True + + def is_pure_color(self): + if self.layout != PLAIN or self.colorspace == ZS: + return False + pures = [channel.pure + for channel in self.le_channels + if channel.type != VOID] + for x in pures: + assert x == pures[0] + return pures[0] + + def channel_type(self): + types = [channel.type + for channel in self.le_channels + if channel.type != VOID] + for x in types: + assert x == types[0] + return types[0] + + def is_pure_signed(self): + return self.is_pure_color() and self.channel_type() == SIGNED + + def is_pure_unsigned(self): + return self.is_pure_color() and self.channel_type() == UNSIGNED + + def has_channel(self, id): + return self.le_swizzles[id] != SWIZZLE_NONE + + def has_depth(self): + return self.colorspace == ZS and self.has_channel(0) + + def has_stencil(self): + return self.colorspace == ZS and self.has_channel(1) + + def stride(self): + return self.block_size()/8 + + +_type_parse_map = { + '': VOID, + 'x': VOID, + 'u': UNSIGNED, + 's': SIGNED, + 'h': FIXED, + 'f': FLOAT, +} + +_swizzle_parse_map = { + 'x': SWIZZLE_X, + 'y': SWIZZLE_Y, + 'z': SWIZZLE_Z, + 'w': SWIZZLE_W, + '0': SWIZZLE_0, + '1': SWIZZLE_1, + '_': SWIZZLE_NONE, +} + +def _parse_channels(fields, layout, colorspace, swizzles): + if layout == PLAIN: + names = ['']*4 + if colorspace in (RGB, SRGB): + for i in range(4): + swizzle = swizzles[i] + if swizzle < 4: + names[swizzle] += 'rgba'[i] + elif colorspace == ZS: + for i in range(4): + swizzle = swizzles[i] + if swizzle < 4: + names[swizzle] += 'zs'[i] + else: + assert False + for i in range(4): + if names[i] == '': + names[i] = 'x' + else: + names = ['x', 'y', 'z', 'w'] + + channels = [] + for i in range(0, 4): + field = fields[i] + if field: + type = _type_parse_map[field[0]] + if field[1] == 'n': + norm = True + pure = False + size = int(field[2:]) + elif field[1] == 'p': + pure = True + norm = False + size = int(field[2:]) + else: + norm = False + pure = False + size = int(field[1:]) + else: + type = VOID + norm = False + pure = False + size = 0 + channel = Channel(type, norm, pure, size, names[i]) + channels.append(channel) + + return channels + +def parse(filename): + '''Parse the format description in CSV format in terms of the + Channel and Format classes above.''' + + stream = open(filename) + formats = [] + for line in stream: + try: + comment = line.index('#') + except ValueError: + pass + else: + line = line[:comment] + line = line.strip() + if not line: + continue + + fields = [field.strip() for field in line.split(',')] + if len (fields) == 10: + fields += fields[4:9] + assert len (fields) == 15 + + name = fields[0] + layout = fields[1] + block_width, block_height = map(int, fields[2:4]) + colorspace = fields[9] + + le_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[8]] + le_channels = _parse_channels(fields[4:8], layout, colorspace, le_swizzles) + + be_swizzles = [_swizzle_parse_map[swizzle] for swizzle in fields[14]] + be_channels = _parse_channels(fields[10:14], layout, colorspace, be_swizzles) + + le_shift = 0 + for channel in le_channels: + channel.shift = le_shift + le_shift += channel.size + + be_shift = 0 + for channel in be_channels[3::-1]: + channel.shift = be_shift + be_shift += channel.size + + assert le_shift == be_shift + for i in range(4): + assert (le_swizzles[i] != SWIZZLE_NONE) == (be_swizzles[i] != SWIZZLE_NONE) + + format = Format(name, layout, block_width, block_height, le_channels, le_swizzles, be_channels, be_swizzles, colorspace) + formats.append(format) + return formats + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_s3tc.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_s3tc.h new file mode 100644 index 0000000..33531a7 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_s3tc.h @@ -0,0 +1,50 @@ +/************************************************************************** + * + * Copyright 2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + **************************************************************************/ + + +#ifndef U_FORMAT_S3TC_H_ +#define U_FORMAT_S3TC_H_ + + +#include "pipe/p_compiler.h" + + +enum util_format_dxtn { + UTIL_FORMAT_DXT1_RGB = 0x83F0, + UTIL_FORMAT_DXT1_RGBA = 0x83F1, + UTIL_FORMAT_DXT3_RGBA = 0x83F2, + UTIL_FORMAT_DXT5_RGBA = 0x83F3 +}; + + +extern boolean util_format_s3tc_enabled; + + +void +util_format_s3tc_init(void); + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_table.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_table.c new file mode 100644 index 0000000..e6e7f44 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_table.c @@ -0,0 +1,10573 @@ +/* This file is autogenerated by u_format_table.py from u_format.csv. Do not edit directly. */ + +/************************************************************************** + * + * Copyright 2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "pipe/p_compiler.h" +#include "u_format.h" +#include "u_half.h" +#include "u_math.h" + +const struct util_format_description +util_format_none_description = { + PIPE_FORMAT_NONE, + "PIPE_FORMAT_NONE", + "none", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b8g8r8a8_unorm_description = { + PIPE_FORMAT_B8G8R8A8_UNORM, + "PIPE_FORMAT_B8G8R8A8_UNORM", + "b8g8r8a8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b8g8r8x8_unorm_description = { + PIPE_FORMAT_B8G8R8X8_UNORM, + "PIPE_FORMAT_B8G8R8X8_UNORM", + "b8g8r8x8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8r8g8b8_unorm_description = { + PIPE_FORMAT_A8R8G8B8_UNORM, + "PIPE_FORMAT_A8R8G8B8_UNORM", + "a8r8g8b8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = b */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_x8r8g8b8_unorm_description = { + PIPE_FORMAT_X8R8G8B8_UNORM, + "PIPE_FORMAT_X8R8G8B8_UNORM", + "x8r8g8b8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = b */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8b8g8r8_unorm_description = { + PIPE_FORMAT_A8B8G8R8_UNORM, + "PIPE_FORMAT_A8B8G8R8_UNORM", + "a8b8g8r8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_x8b8g8r8_unorm_description = { + PIPE_FORMAT_X8B8G8R8_UNORM, + "PIPE_FORMAT_X8B8G8R8_UNORM", + "x8b8g8r8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8x8_unorm_description = { + PIPE_FORMAT_R8G8B8X8_UNORM, + "PIPE_FORMAT_R8G8B8X8_UNORM", + "r8g8b8x8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b5g5r5x1_unorm_description = { + PIPE_FORMAT_B5G5R5X1_UNORM, + "PIPE_FORMAT_B5G5R5X1_UNORM", + "b5g5r5x1_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 1, 15}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 10}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 5}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 10}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 1, 15} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b5g5r5a1_unorm_description = { + PIPE_FORMAT_B5G5R5A1_UNORM, + "PIPE_FORMAT_B5G5R5A1_UNORM", + "b5g5r5a1_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 1, 15}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 10}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 5}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 10}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 1, 15} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b4g4r4a4_unorm_description = { + PIPE_FORMAT_B4G4R4A4_UNORM, + "PIPE_FORMAT_B4G4R4A4_UNORM", + "b4g4r4a4_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 12}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 8}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 8}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 12} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b4g4r4x4_unorm_description = { + PIPE_FORMAT_B4G4R4X4_UNORM, + "PIPE_FORMAT_B4G4R4X4_UNORM", + "b4g4r4x4_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 4, 12}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 8}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 8}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 4, 12} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a4b4g4r4_unorm_description = { + PIPE_FORMAT_A4B4G4R4_UNORM, + "PIPE_FORMAT_A4B4G4R4_UNORM", + "a4b4g4r4_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 12}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 12} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b5g6r5_unorm_description = { + PIPE_FORMAT_B5G6R5_UNORM, + "PIPE_FORMAT_B5G6R5_UNORM", + "b5g6r5_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 11}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 6, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 6, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 11}, /* z = r */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10a2_unorm_description = { + PIPE_FORMAT_R10G10B10A2_UNORM, + "PIPE_FORMAT_R10G10B10A2_UNORM", + "r10g10b10a2_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10x2_unorm_description = { + PIPE_FORMAT_R10G10B10X2_UNORM, + "PIPE_FORMAT_R10G10B10X2_UNORM", + "r10g10b10x2_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b10g10r10a2_unorm_description = { + PIPE_FORMAT_B10G10R10A2_UNORM, + "PIPE_FORMAT_B10G10R10A2_UNORM", + "b10g10r10a2_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b2g3r3_unorm_description = { + PIPE_FORMAT_B2G3R3_UNORM, + "PIPE_FORMAT_B2G3R3_UNORM", + "b2g3r3_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 3, 5}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 3, 2}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 3, 2}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 3, 5}, /* z = r */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8_unorm_description = { + PIPE_FORMAT_L8_UNORM, + "PIPE_FORMAT_L8_UNORM", + "l8_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8_unorm_description = { + PIPE_FORMAT_A8_UNORM, + "PIPE_FORMAT_A8_UNORM", + "a8_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i8_unorm_description = { + PIPE_FORMAT_I8_UNORM, + "PIPE_FORMAT_I8_UNORM", + "i8_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l4a4_unorm_description = { + PIPE_FORMAT_L4A4_UNORM, + "PIPE_FORMAT_L4A4_UNORM", + "l4a4_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8a8_unorm_description = { + PIPE_FORMAT_L8A8_UNORM, + "PIPE_FORMAT_L8A8_UNORM", + "l8a8_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16_unorm_description = { + PIPE_FORMAT_L16_UNORM, + "PIPE_FORMAT_L16_UNORM", + "l16_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a16_unorm_description = { + PIPE_FORMAT_A16_UNORM, + "PIPE_FORMAT_A16_UNORM", + "a16_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i16_unorm_description = { + PIPE_FORMAT_I16_UNORM, + "PIPE_FORMAT_I16_UNORM", + "i16_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16a16_unorm_description = { + PIPE_FORMAT_L16A16_UNORM, + "PIPE_FORMAT_L16A16_UNORM", + "l16a16_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8_snorm_description = { + PIPE_FORMAT_A8_SNORM, + "PIPE_FORMAT_A8_SNORM", + "a8_snorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8_snorm_description = { + PIPE_FORMAT_L8_SNORM, + "PIPE_FORMAT_L8_SNORM", + "l8_snorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8a8_snorm_description = { + PIPE_FORMAT_L8A8_SNORM, + "PIPE_FORMAT_L8A8_SNORM", + "l8a8_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i8_snorm_description = { + PIPE_FORMAT_I8_SNORM, + "PIPE_FORMAT_I8_SNORM", + "i8_snorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a16_snorm_description = { + PIPE_FORMAT_A16_SNORM, + "PIPE_FORMAT_A16_SNORM", + "a16_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16_snorm_description = { + PIPE_FORMAT_L16_SNORM, + "PIPE_FORMAT_L16_SNORM", + "l16_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16a16_snorm_description = { + PIPE_FORMAT_L16A16_SNORM, + "PIPE_FORMAT_L16A16_SNORM", + "l16a16_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i16_snorm_description = { + PIPE_FORMAT_I16_SNORM, + "PIPE_FORMAT_I16_SNORM", + "i16_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a16_float_description = { + PIPE_FORMAT_A16_FLOAT, + "PIPE_FORMAT_A16_FLOAT", + "a16_float", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16_float_description = { + PIPE_FORMAT_L16_FLOAT, + "PIPE_FORMAT_L16_FLOAT", + "l16_float", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16a16_float_description = { + PIPE_FORMAT_L16A16_FLOAT, + "PIPE_FORMAT_L16A16_FLOAT", + "l16a16_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* x = rgb */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i16_float_description = { + PIPE_FORMAT_I16_FLOAT, + "PIPE_FORMAT_I16_FLOAT", + "i16_float", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a32_float_description = { + PIPE_FORMAT_A32_FLOAT, + "PIPE_FORMAT_A32_FLOAT", + "a32_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l32_float_description = { + PIPE_FORMAT_L32_FLOAT, + "PIPE_FORMAT_L32_FLOAT", + "l32_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l32a32_float_description = { + PIPE_FORMAT_L32A32_FLOAT, + "PIPE_FORMAT_L32A32_FLOAT", + "l32a32_float", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* x = rgb */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i32_float_description = { + PIPE_FORMAT_I32_FLOAT, + "PIPE_FORMAT_I32_FLOAT", + "i32_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8_srgb_description = { + PIPE_FORMAT_L8_SRGB, + "PIPE_FORMAT_L8_SRGB", + "l8_srgb", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_X, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_r8_srgb_description = { + PIPE_FORMAT_R8_SRGB, + "PIPE_FORMAT_R8_SRGB", + "r8_srgb", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_0, /* sg */ + UTIL_FORMAT_SWIZZLE_0, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_l8a8_srgb_description = { + PIPE_FORMAT_L8A8_SRGB, + "PIPE_FORMAT_L8A8_SRGB", + "l8a8_srgb", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_X, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_X, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_r8g8b8_srgb_description = { + PIPE_FORMAT_R8G8B8_SRGB, + "PIPE_FORMAT_R8G8B8_SRGB", + "r8g8b8_srgb", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_srgb_description = { + PIPE_FORMAT_R8G8B8A8_SRGB, + "PIPE_FORMAT_R8G8B8A8_SRGB", + "r8g8b8a8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_a8b8g8r8_srgb_description = { + PIPE_FORMAT_A8B8G8R8_SRGB, + "PIPE_FORMAT_A8B8G8R8_SRGB", + "a8b8g8r8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_Y, /* sb */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_Y, /* sb */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_x8b8g8r8_srgb_description = { + PIPE_FORMAT_X8B8G8R8_SRGB, + "PIPE_FORMAT_X8B8G8R8_SRGB", + "x8b8g8r8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_Y, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_Y, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_b8g8r8a8_srgb_description = { + PIPE_FORMAT_B8G8R8A8_SRGB, + "PIPE_FORMAT_B8G8R8A8_SRGB", + "b8g8r8a8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Z, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_b8g8r8x8_srgb_description = { + PIPE_FORMAT_B8G8R8X8_SRGB, + "PIPE_FORMAT_B8G8R8X8_SRGB", + "b8g8r8x8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Z, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_a8r8g8b8_srgb_description = { + PIPE_FORMAT_A8R8G8B8_SRGB, + "PIPE_FORMAT_A8R8G8B8_SRGB", + "a8r8g8b8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = b */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_W, /* sb */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_W, /* sb */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_x8r8g8b8_srgb_description = { + PIPE_FORMAT_X8R8G8B8_SRGB, + "PIPE_FORMAT_X8R8G8B8_SRGB", + "x8r8g8b8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = b */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_W, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* sr */ + UTIL_FORMAT_SWIZZLE_Z, /* sg */ + UTIL_FORMAT_SWIZZLE_W, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_r8sg8sb8ux8u_norm_description = { + PIPE_FORMAT_R8SG8SB8UX8U_NORM, + "PIPE_FORMAT_R8SG8SB8UX8U_NORM", + "r8sg8sb8ux8u_norm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + TRUE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10sg10sb10sa2u_norm_description = { + PIPE_FORMAT_R10SG10SB10SA2U_NORM, + "PIPE_FORMAT_R10SG10SB10SA2U_NORM", + "r10sg10sb10sa2u_norm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + TRUE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r5sg5sb6u_norm_description = { + PIPE_FORMAT_R5SG5SB6U_NORM, + "PIPE_FORMAT_R5SG5SB6U_NORM", + "r5sg5sb6u_norm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + TRUE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 6, 10}, /* x = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 5, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 5, 0}, /* z = r */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 5, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 5, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 6, 10}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_s8_uint_description = { + PIPE_FORMAT_S8_UINT, + "PIPE_FORMAT_S8_UINT", + "s8_uint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = s */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_X, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_z16_unorm_description = { + PIPE_FORMAT_Z16_UNORM, + "PIPE_FORMAT_Z16_UNORM", + "z16_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_z32_unorm_description = { + PIPE_FORMAT_Z32_UNORM, + "PIPE_FORMAT_Z32_UNORM", + "z32_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* x = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_z32_float_description = { + PIPE_FORMAT_Z32_FLOAT, + "PIPE_FORMAT_Z32_FLOAT", + "z32_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_z24_unorm_s8_uint_description = { + PIPE_FORMAT_Z24_UNORM_S8_UINT, + "PIPE_FORMAT_Z24_UNORM_S8_UINT", + "z24_unorm_s8_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + TRUE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24}, /* x = s */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 0}, /* y = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 0}, /* x = z */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24}, /* y = s */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* z */ + UTIL_FORMAT_SWIZZLE_X, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_Y, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_s8_uint_z24_unorm_description = { + PIPE_FORMAT_S8_UINT_Z24_UNORM, + "PIPE_FORMAT_S8_UINT_Z24_UNORM", + "s8_uint_z24_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + TRUE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 8}, /* x = z */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* y = s */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = s */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 8}, /* y = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_Y, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* z */ + UTIL_FORMAT_SWIZZLE_X, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_x24s8_uint_description = { + PIPE_FORMAT_X24S8_UINT, + "PIPE_FORMAT_X24S8_UINT", + "x24s8_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24}, /* x = s */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 0}, /* y = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24}, /* y = s */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_X, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_Y, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_s8x24_uint_description = { + PIPE_FORMAT_S8X24_UINT, + "PIPE_FORMAT_S8X24_UINT", + "s8x24_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 8}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* y = s */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = s */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 8}, /* y = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_Y, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_X, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_z24x8_unorm_description = { + PIPE_FORMAT_Z24X8_UNORM, + "PIPE_FORMAT_Z24X8_UNORM", + "z24x8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 0}, /* y = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 0}, /* x = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* y = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_x8z24_unorm_description = { + PIPE_FORMAT_X8Z24_UNORM, + "PIPE_FORMAT_X8Z24_UNORM", + "x8z24_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 8}, /* x = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* y = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 24, 8}, /* y = z */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* z */ + UTIL_FORMAT_SWIZZLE_NONE, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_z32_float_s8x24_uint_description = { + PIPE_FORMAT_Z32_FLOAT_S8X24_UINT, + "PIPE_FORMAT_Z32_FLOAT_S8X24_UINT", + "z32_float_s8x24_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + TRUE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* x = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 8}, /* y = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* z = s */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = z */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 32}, /* y = s */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 40}, /* z = x */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_Z, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* z */ + UTIL_FORMAT_SWIZZLE_Y, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_x32_s8x24_uint_description = { + PIPE_FORMAT_X32_S8X24_UINT, + "PIPE_FORMAT_X32_S8X24_UINT", + "x32_s8x24_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 32}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 8}, /* y = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* z = s */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 32}, /* y = s */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 24, 40}, /* z = x */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_Z, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_NONE, /* z */ + UTIL_FORMAT_SWIZZLE_Y, /* s */ + UTIL_FORMAT_SWIZZLE_NONE, /* ignored */ + UTIL_FORMAT_SWIZZLE_NONE /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_ZS, +}; + +const struct util_format_description +util_format_uyvy_description = { + PIPE_FORMAT_UYVY, + "PIPE_FORMAT_UYVY", + "uyvy", + {2, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_1 /* ignored */ + }, + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_yuyv_description = { + PIPE_FORMAT_YUYV, + "PIPE_FORMAT_YUYV", + "yuyv", + {2, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_1 /* ignored */ + }, + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_r8g8_b8g8_unorm_description = { + PIPE_FORMAT_R8G8_B8G8_UNORM, + "PIPE_FORMAT_R8G8_B8G8_UNORM", + "r8g8_b8g8_unorm", + {2, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_g8r8_g8b8_unorm_description = { + PIPE_FORMAT_G8R8_G8B8_UNORM, + "PIPE_FORMAT_G8R8_G8B8_UNORM", + "g8r8_g8b8_unorm", + {2, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_g8r8_b8r8_unorm_description = { + PIPE_FORMAT_G8R8_B8R8_UNORM, + "PIPE_FORMAT_G8R8_B8R8_UNORM", + "g8r8_b8r8_unorm", + {2, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_r8b8_unorm_description = { + PIPE_FORMAT_R8G8_R8B8_UNORM, + "PIPE_FORMAT_R8G8_R8B8_UNORM", + "r8g8_r8b8_unorm", + {2, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_SUBSAMPLED, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r11g11b10_float_description = { + PIPE_FORMAT_R11G11B10_FLOAT, + "PIPE_FORMAT_R11G11B10_FLOAT", + "r11g11b10_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r9g9b9e5_float_description = { + PIPE_FORMAT_R9G9B9E5_FLOAT, + "PIPE_FORMAT_R9G9B9E5_FLOAT", + "r9g9b9e5_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r1_unorm_description = { + PIPE_FORMAT_R1_UNORM, + "PIPE_FORMAT_R1_UNORM", + "r1_unorm", + {8, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8bx_snorm_description = { + PIPE_FORMAT_R8G8Bx_SNORM, + "PIPE_FORMAT_R8G8Bx_SNORM", + "r8g8bx_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 2, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* x = x */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* y = y */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = y */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_dxt1_rgb_description = { + PIPE_FORMAT_DXT1_RGB, + "PIPE_FORMAT_DXT1_RGB", + "dxt1_rgb", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_dxt1_rgba_description = { + PIPE_FORMAT_DXT1_RGBA, + "PIPE_FORMAT_DXT1_RGBA", + "dxt1_rgba", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_dxt3_rgba_description = { + PIPE_FORMAT_DXT3_RGBA, + "PIPE_FORMAT_DXT3_RGBA", + "dxt3_rgba", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_dxt5_rgba_description = { + PIPE_FORMAT_DXT5_RGBA, + "PIPE_FORMAT_DXT5_RGBA", + "dxt5_rgba", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_dxt1_srgb_description = { + PIPE_FORMAT_DXT1_SRGB, + "PIPE_FORMAT_DXT1_SRGB", + "dxt1_srgb", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_dxt1_srgba_description = { + PIPE_FORMAT_DXT1_SRGBA, + "PIPE_FORMAT_DXT1_SRGBA", + "dxt1_srgba", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_dxt3_srgba_description = { + PIPE_FORMAT_DXT3_SRGBA, + "PIPE_FORMAT_DXT3_SRGBA", + "dxt3_srgba", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_dxt5_srgba_description = { + PIPE_FORMAT_DXT5_SRGBA, + "PIPE_FORMAT_DXT5_SRGBA", + "dxt5_srgba", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_S3TC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_rgtc1_unorm_description = { + PIPE_FORMAT_RGTC1_UNORM, + "PIPE_FORMAT_RGTC1_UNORM", + "rgtc1_unorm", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_rgtc1_snorm_description = { + PIPE_FORMAT_RGTC1_SNORM, + "PIPE_FORMAT_RGTC1_SNORM", + "rgtc1_snorm", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_rgtc2_unorm_description = { + PIPE_FORMAT_RGTC2_UNORM, + "PIPE_FORMAT_RGTC2_UNORM", + "rgtc2_unorm", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_rgtc2_snorm_description = { + PIPE_FORMAT_RGTC2_SNORM, + "PIPE_FORMAT_RGTC2_SNORM", + "rgtc2_snorm", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_latc1_unorm_description = { + PIPE_FORMAT_LATC1_UNORM, + "PIPE_FORMAT_LATC1_UNORM", + "latc1_unorm", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_latc1_snorm_description = { + PIPE_FORMAT_LATC1_SNORM, + "PIPE_FORMAT_LATC1_SNORM", + "latc1_snorm", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_latc2_unorm_description = { + PIPE_FORMAT_LATC2_UNORM, + "PIPE_FORMAT_LATC2_UNORM", + "latc2_unorm", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_latc2_snorm_description = { + PIPE_FORMAT_LATC2_SNORM, + "PIPE_FORMAT_LATC2_SNORM", + "latc2_snorm", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_RGTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_etc1_rgb8_description = { + PIPE_FORMAT_ETC1_RGB8, + "PIPE_FORMAT_ETC1_RGB8", + "etc1_rgb8", + {4, 4, 64}, /* block */ + UTIL_FORMAT_LAYOUT_ETC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 64, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_bptc_rgba_unorm_description = { + PIPE_FORMAT_BPTC_RGBA_UNORM, + "PIPE_FORMAT_BPTC_RGBA_UNORM", + "bptc_rgba_unorm", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_BPTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_bptc_srgba_description = { + PIPE_FORMAT_BPTC_SRGBA, + "PIPE_FORMAT_BPTC_SRGBA", + "bptc_srgba", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_BPTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_bptc_rgb_float_description = { + PIPE_FORMAT_BPTC_RGB_FLOAT, + "PIPE_FORMAT_BPTC_RGB_FLOAT", + "bptc_rgb_float", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_BPTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_bptc_rgb_ufloat_description = { + PIPE_FORMAT_BPTC_RGB_UFLOAT, + "PIPE_FORMAT_BPTC_RGB_UFLOAT", + "bptc_rgb_ufloat", + {4, 4, 128}, /* block */ + UTIL_FORMAT_LAYOUT_BPTC, + 1, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 128, 0}, /* x = x */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r64_float_description = { + PIPE_FORMAT_R64_FLOAT, + "PIPE_FORMAT_R64_FLOAT", + "r64_float", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r64g64_float_description = { + PIPE_FORMAT_R64G64_FLOAT, + "PIPE_FORMAT_R64G64_FLOAT", + "r64g64_float", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 64}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r64g64b64_float_description = { + PIPE_FORMAT_R64G64B64_FLOAT, + "PIPE_FORMAT_R64G64B64_FLOAT", + "r64g64b64_float", + {1, 1, 192}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 128}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 128}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r64g64b64a64_float_description = { + PIPE_FORMAT_R64G64B64A64_FLOAT, + "PIPE_FORMAT_R64G64B64A64_FLOAT", + "r64g64b64a64_float", + {1, 1, 256}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 192}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 128}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 128}, /* z = b */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 64, 192} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_float_description = { + PIPE_FORMAT_R32_FLOAT, + "PIPE_FORMAT_R32_FLOAT", + "r32_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_float_description = { + PIPE_FORMAT_R32G32_FLOAT, + "PIPE_FORMAT_R32G32_FLOAT", + "r32g32_float", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_float_description = { + PIPE_FORMAT_R32G32B32_FLOAT, + "PIPE_FORMAT_R32G32B32_FLOAT", + "r32g32b32_float", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_float_description = { + PIPE_FORMAT_R32G32B32A32_FLOAT, + "PIPE_FORMAT_R32G32B32A32_FLOAT", + "r32g32b32a32_float", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_unorm_description = { + PIPE_FORMAT_R32_UNORM, + "PIPE_FORMAT_R32_UNORM", + "r32_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_unorm_description = { + PIPE_FORMAT_R32G32_UNORM, + "PIPE_FORMAT_R32G32_UNORM", + "r32g32_unorm", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_unorm_description = { + PIPE_FORMAT_R32G32B32_UNORM, + "PIPE_FORMAT_R32G32B32_UNORM", + "r32g32b32_unorm", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_unorm_description = { + PIPE_FORMAT_R32G32B32A32_UNORM, + "PIPE_FORMAT_R32G32B32A32_UNORM", + "r32g32b32a32_unorm", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_uscaled_description = { + PIPE_FORMAT_R32_USCALED, + "PIPE_FORMAT_R32_USCALED", + "r32_uscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_uscaled_description = { + PIPE_FORMAT_R32G32_USCALED, + "PIPE_FORMAT_R32G32_USCALED", + "r32g32_uscaled", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_uscaled_description = { + PIPE_FORMAT_R32G32B32_USCALED, + "PIPE_FORMAT_R32G32B32_USCALED", + "r32g32b32_uscaled", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_uscaled_description = { + PIPE_FORMAT_R32G32B32A32_USCALED, + "PIPE_FORMAT_R32G32B32A32_USCALED", + "r32g32b32a32_uscaled", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_snorm_description = { + PIPE_FORMAT_R32_SNORM, + "PIPE_FORMAT_R32_SNORM", + "r32_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_snorm_description = { + PIPE_FORMAT_R32G32_SNORM, + "PIPE_FORMAT_R32G32_SNORM", + "r32g32_snorm", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_snorm_description = { + PIPE_FORMAT_R32G32B32_SNORM, + "PIPE_FORMAT_R32G32B32_SNORM", + "r32g32b32_snorm", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_snorm_description = { + PIPE_FORMAT_R32G32B32A32_SNORM, + "PIPE_FORMAT_R32G32B32A32_SNORM", + "r32g32b32a32_snorm", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_sscaled_description = { + PIPE_FORMAT_R32_SSCALED, + "PIPE_FORMAT_R32_SSCALED", + "r32_sscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_sscaled_description = { + PIPE_FORMAT_R32G32_SSCALED, + "PIPE_FORMAT_R32G32_SSCALED", + "r32g32_sscaled", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_sscaled_description = { + PIPE_FORMAT_R32G32B32_SSCALED, + "PIPE_FORMAT_R32G32B32_SSCALED", + "r32g32b32_sscaled", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_sscaled_description = { + PIPE_FORMAT_R32G32B32A32_SSCALED, + "PIPE_FORMAT_R32G32B32A32_SSCALED", + "r32g32b32a32_sscaled", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_float_description = { + PIPE_FORMAT_R16_FLOAT, + "PIPE_FORMAT_R16_FLOAT", + "r16_float", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_float_description = { + PIPE_FORMAT_R16G16_FLOAT, + "PIPE_FORMAT_R16G16_FLOAT", + "r16g16_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_float_description = { + PIPE_FORMAT_R16G16B16_FLOAT, + "PIPE_FORMAT_R16G16B16_FLOAT", + "r16g16b16_float", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_float_description = { + PIPE_FORMAT_R16G16B16A16_FLOAT, + "PIPE_FORMAT_R16G16B16A16_FLOAT", + "r16g16b16a16_float", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_unorm_description = { + PIPE_FORMAT_R16_UNORM, + "PIPE_FORMAT_R16_UNORM", + "r16_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_unorm_description = { + PIPE_FORMAT_R16G16_UNORM, + "PIPE_FORMAT_R16G16_UNORM", + "r16g16_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_unorm_description = { + PIPE_FORMAT_R16G16B16_UNORM, + "PIPE_FORMAT_R16G16B16_UNORM", + "r16g16b16_unorm", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_unorm_description = { + PIPE_FORMAT_R16G16B16A16_UNORM, + "PIPE_FORMAT_R16G16B16A16_UNORM", + "r16g16b16a16_unorm", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_uscaled_description = { + PIPE_FORMAT_R16_USCALED, + "PIPE_FORMAT_R16_USCALED", + "r16_uscaled", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_uscaled_description = { + PIPE_FORMAT_R16G16_USCALED, + "PIPE_FORMAT_R16G16_USCALED", + "r16g16_uscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_uscaled_description = { + PIPE_FORMAT_R16G16B16_USCALED, + "PIPE_FORMAT_R16G16B16_USCALED", + "r16g16b16_uscaled", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_uscaled_description = { + PIPE_FORMAT_R16G16B16A16_USCALED, + "PIPE_FORMAT_R16G16B16A16_USCALED", + "r16g16b16a16_uscaled", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_snorm_description = { + PIPE_FORMAT_R16_SNORM, + "PIPE_FORMAT_R16_SNORM", + "r16_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_snorm_description = { + PIPE_FORMAT_R16G16_SNORM, + "PIPE_FORMAT_R16G16_SNORM", + "r16g16_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_snorm_description = { + PIPE_FORMAT_R16G16B16_SNORM, + "PIPE_FORMAT_R16G16B16_SNORM", + "r16g16b16_snorm", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_snorm_description = { + PIPE_FORMAT_R16G16B16A16_SNORM, + "PIPE_FORMAT_R16G16B16A16_SNORM", + "r16g16b16a16_snorm", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_sscaled_description = { + PIPE_FORMAT_R16_SSCALED, + "PIPE_FORMAT_R16_SSCALED", + "r16_sscaled", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_sscaled_description = { + PIPE_FORMAT_R16G16_SSCALED, + "PIPE_FORMAT_R16G16_SSCALED", + "r16g16_sscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_sscaled_description = { + PIPE_FORMAT_R16G16B16_SSCALED, + "PIPE_FORMAT_R16G16B16_SSCALED", + "r16g16b16_sscaled", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_sscaled_description = { + PIPE_FORMAT_R16G16B16A16_SSCALED, + "PIPE_FORMAT_R16G16B16A16_SSCALED", + "r16g16b16a16_sscaled", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8_unorm_description = { + PIPE_FORMAT_R8_UNORM, + "PIPE_FORMAT_R8_UNORM", + "r8_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_unorm_description = { + PIPE_FORMAT_R8G8_UNORM, + "PIPE_FORMAT_R8G8_UNORM", + "r8g8_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8_unorm_description = { + PIPE_FORMAT_R8G8B8_UNORM, + "PIPE_FORMAT_R8G8B8_UNORM", + "r8g8b8_unorm", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_unorm_description = { + PIPE_FORMAT_R8G8B8A8_UNORM, + "PIPE_FORMAT_R8G8B8A8_UNORM", + "r8g8b8a8_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8_uscaled_description = { + PIPE_FORMAT_R8_USCALED, + "PIPE_FORMAT_R8_USCALED", + "r8_uscaled", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_uscaled_description = { + PIPE_FORMAT_R8G8_USCALED, + "PIPE_FORMAT_R8G8_USCALED", + "r8g8_uscaled", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8_uscaled_description = { + PIPE_FORMAT_R8G8B8_USCALED, + "PIPE_FORMAT_R8G8B8_USCALED", + "r8g8b8_uscaled", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_uscaled_description = { + PIPE_FORMAT_R8G8B8A8_USCALED, + "PIPE_FORMAT_R8G8B8A8_USCALED", + "r8g8b8a8_uscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8_snorm_description = { + PIPE_FORMAT_R8_SNORM, + "PIPE_FORMAT_R8_SNORM", + "r8_snorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_snorm_description = { + PIPE_FORMAT_R8G8_SNORM, + "PIPE_FORMAT_R8G8_SNORM", + "r8g8_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8_snorm_description = { + PIPE_FORMAT_R8G8B8_SNORM, + "PIPE_FORMAT_R8G8B8_SNORM", + "r8g8b8_snorm", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_snorm_description = { + PIPE_FORMAT_R8G8B8A8_SNORM, + "PIPE_FORMAT_R8G8B8A8_SNORM", + "r8g8b8a8_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8_sscaled_description = { + PIPE_FORMAT_R8_SSCALED, + "PIPE_FORMAT_R8_SSCALED", + "r8_sscaled", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_sscaled_description = { + PIPE_FORMAT_R8G8_SSCALED, + "PIPE_FORMAT_R8G8_SSCALED", + "r8g8_sscaled", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8_sscaled_description = { + PIPE_FORMAT_R8G8B8_SSCALED, + "PIPE_FORMAT_R8G8B8_SSCALED", + "r8g8b8_sscaled", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_sscaled_description = { + PIPE_FORMAT_R8G8B8A8_SSCALED, + "PIPE_FORMAT_R8G8B8A8_SSCALED", + "r8g8b8a8_sscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_fixed_description = { + PIPE_FORMAT_R32_FIXED, + "PIPE_FORMAT_R32_FIXED", + "r32_fixed", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_fixed_description = { + PIPE_FORMAT_R32G32_FIXED, + "PIPE_FORMAT_R32G32_FIXED", + "r32g32_fixed", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_fixed_description = { + PIPE_FORMAT_R32G32B32_FIXED, + "PIPE_FORMAT_R32G32B32_FIXED", + "r32g32b32_fixed", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_fixed_description = { + PIPE_FORMAT_R32G32B32A32_FIXED, + "PIPE_FORMAT_R32G32B32A32_FIXED", + "r32g32b32a32_fixed", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_FIXED, FALSE, FALSE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10x2_uscaled_description = { + PIPE_FORMAT_R10G10B10X2_USCALED, + "PIPE_FORMAT_R10G10B10X2_USCALED", + "r10g10b10x2_uscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10x2_snorm_description = { + PIPE_FORMAT_R10G10B10X2_SNORM, + "PIPE_FORMAT_R10G10B10X2_SNORM", + "r10g10b10x2_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30}, /* x = x */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_yv12_description = { + PIPE_FORMAT_YV12, + "PIPE_FORMAT_YV12", + "yv12", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 4, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = w */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = w */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_yv16_description = { + PIPE_FORMAT_YV16, + "PIPE_FORMAT_YV16", + "yv16", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 4, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = w */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = w */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_iyuv_description = { + PIPE_FORMAT_IYUV, + "PIPE_FORMAT_IYUV", + "iyuv", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 4, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = w */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = w */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_nv12_description = { + PIPE_FORMAT_NV12, + "PIPE_FORMAT_NV12", + "nv12", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 4, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = w */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = w */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_nv21_description = { + PIPE_FORMAT_NV21, + "PIPE_FORMAT_NV21", + "nv21", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_OTHER, + 4, /* nr_channels */ + FALSE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = w */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 8}, /* y = y */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 16}, /* z = z */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = w */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* y */ + UTIL_FORMAT_SWIZZLE_Y, /* u */ + UTIL_FORMAT_SWIZZLE_Z, /* v */ + UTIL_FORMAT_SWIZZLE_W /* ignored */ + }, +#endif + UTIL_FORMAT_COLORSPACE_YUV, +}; + +const struct util_format_description +util_format_a4r4_unorm_description = { + PIPE_FORMAT_A4R4_UNORM, + "PIPE_FORMAT_A4R4_UNORM", + "a4r4_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r4a4_unorm_description = { + PIPE_FORMAT_R4A4_UNORM, + "PIPE_FORMAT_R4A4_UNORM", + "r4a4_unorm", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 4, 4}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8a8_unorm_description = { + PIPE_FORMAT_R8A8_UNORM, + "PIPE_FORMAT_R8A8_UNORM", + "r8a8_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8r8_unorm_description = { + PIPE_FORMAT_A8R8_UNORM, + "PIPE_FORMAT_A8R8_UNORM", + "a8r8_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10a2_uscaled_description = { + PIPE_FORMAT_R10G10B10A2_USCALED, + "PIPE_FORMAT_R10G10B10A2_USCALED", + "r10g10b10a2_uscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10a2_sscaled_description = { + PIPE_FORMAT_R10G10B10A2_SSCALED, + "PIPE_FORMAT_R10G10B10A2_SSCALED", + "r10g10b10a2_sscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10a2_snorm_description = { + PIPE_FORMAT_R10G10B10A2_SNORM, + "PIPE_FORMAT_R10G10B10A2_SNORM", + "r10g10b10a2_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b10g10r10a2_uscaled_description = { + PIPE_FORMAT_B10G10R10A2_USCALED, + "PIPE_FORMAT_B10G10R10A2_USCALED", + "b10g10r10a2_uscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 20}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 10, 20}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b10g10r10a2_sscaled_description = { + PIPE_FORMAT_B10G10R10A2_SSCALED, + "PIPE_FORMAT_B10G10R10A2_SSCALED", + "b10g10r10a2_sscaled", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 20}, /* y = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 10, 20}, /* z = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b10g10r10a2_snorm_description = { + PIPE_FORMAT_B10G10R10A2_SNORM, + "PIPE_FORMAT_B10G10R10A2_SNORM", + "b10g10r10a2_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* y = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 10, 20}, /* z = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8_uint_description = { + PIPE_FORMAT_R8_UINT, + "PIPE_FORMAT_R8_UINT", + "r8_uint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_uint_description = { + PIPE_FORMAT_R8G8_UINT, + "PIPE_FORMAT_R8G8_UINT", + "r8g8_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8_uint_description = { + PIPE_FORMAT_R8G8B8_UINT, + "PIPE_FORMAT_R8G8B8_UINT", + "r8g8b8_uint", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_uint_description = { + PIPE_FORMAT_R8G8B8A8_UINT, + "PIPE_FORMAT_R8G8B8A8_UINT", + "r8g8b8a8_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8_sint_description = { + PIPE_FORMAT_R8_SINT, + "PIPE_FORMAT_R8_SINT", + "r8_sint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8_sint_description = { + PIPE_FORMAT_R8G8_SINT, + "PIPE_FORMAT_R8G8_SINT", + "r8g8_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8_sint_description = { + PIPE_FORMAT_R8G8B8_SINT, + "PIPE_FORMAT_R8G8B8_SINT", + "r8g8b8_sint", + {1, 1, 24}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 16}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8a8_sint_description = { + PIPE_FORMAT_R8G8B8A8_SINT, + "PIPE_FORMAT_R8G8B8A8_SINT", + "r8g8b8a8_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 24} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_uint_description = { + PIPE_FORMAT_R16_UINT, + "PIPE_FORMAT_R16_UINT", + "r16_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_uint_description = { + PIPE_FORMAT_R16G16_UINT, + "PIPE_FORMAT_R16G16_UINT", + "r16g16_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_uint_description = { + PIPE_FORMAT_R16G16B16_UINT, + "PIPE_FORMAT_R16G16B16_UINT", + "r16g16b16_uint", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_uint_description = { + PIPE_FORMAT_R16G16B16A16_UINT, + "PIPE_FORMAT_R16G16B16A16_UINT", + "r16g16b16a16_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16_sint_description = { + PIPE_FORMAT_R16_SINT, + "PIPE_FORMAT_R16_SINT", + "r16_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16_sint_description = { + PIPE_FORMAT_R16G16_SINT, + "PIPE_FORMAT_R16G16_SINT", + "r16g16_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16_sint_description = { + PIPE_FORMAT_R16G16B16_SINT, + "PIPE_FORMAT_R16G16B16_SINT", + "r16g16b16_sint", + {1, 1, 48}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 32}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16a16_sint_description = { + PIPE_FORMAT_R16G16B16A16_SINT, + "PIPE_FORMAT_R16G16B16A16_SINT", + "r16g16b16a16_sint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 48} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_uint_description = { + PIPE_FORMAT_R32_UINT, + "PIPE_FORMAT_R32_UINT", + "r32_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_uint_description = { + PIPE_FORMAT_R32G32_UINT, + "PIPE_FORMAT_R32G32_UINT", + "r32g32_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_uint_description = { + PIPE_FORMAT_R32G32B32_UINT, + "PIPE_FORMAT_R32G32B32_UINT", + "r32g32b32_uint", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_uint_description = { + PIPE_FORMAT_R32G32B32A32_UINT, + "PIPE_FORMAT_R32G32B32A32_UINT", + "r32g32b32a32_uint", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32_sint_description = { + PIPE_FORMAT_R32_SINT, + "PIPE_FORMAT_R32_SINT", + "r32_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32_sint_description = { + PIPE_FORMAT_R32G32_SINT, + "PIPE_FORMAT_R32G32_SINT", + "r32g32_sint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32_sint_description = { + PIPE_FORMAT_R32G32B32_SINT, + "PIPE_FORMAT_R32G32B32_SINT", + "r32g32b32_sint", + {1, 1, 96}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 64}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 64}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32a32_sint_description = { + PIPE_FORMAT_R32G32B32A32_SINT, + "PIPE_FORMAT_R32G32B32A32_SINT", + "r32g32b32a32_sint", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0} /* w = a */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 96} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8_uint_description = { + PIPE_FORMAT_A8_UINT, + "PIPE_FORMAT_A8_UINT", + "a8_uint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i8_uint_description = { + PIPE_FORMAT_I8_UINT, + "PIPE_FORMAT_I8_UINT", + "i8_uint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8_uint_description = { + PIPE_FORMAT_L8_UINT, + "PIPE_FORMAT_L8_UINT", + "l8_uint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8a8_uint_description = { + PIPE_FORMAT_L8A8_UINT, + "PIPE_FORMAT_L8A8_UINT", + "l8a8_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8_sint_description = { + PIPE_FORMAT_A8_SINT, + "PIPE_FORMAT_A8_SINT", + "a8_sint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i8_sint_description = { + PIPE_FORMAT_I8_SINT, + "PIPE_FORMAT_I8_SINT", + "i8_sint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8_sint_description = { + PIPE_FORMAT_L8_SINT, + "PIPE_FORMAT_L8_SINT", + "l8_sint", + {1, 1, 8}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l8a8_sint_description = { + PIPE_FORMAT_L8A8_SINT, + "PIPE_FORMAT_L8A8_SINT", + "l8a8_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a16_uint_description = { + PIPE_FORMAT_A16_UINT, + "PIPE_FORMAT_A16_UINT", + "a16_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i16_uint_description = { + PIPE_FORMAT_I16_UINT, + "PIPE_FORMAT_I16_UINT", + "i16_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16_uint_description = { + PIPE_FORMAT_L16_UINT, + "PIPE_FORMAT_L16_UINT", + "l16_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16a16_uint_description = { + PIPE_FORMAT_L16A16_UINT, + "PIPE_FORMAT_L16A16_UINT", + "l16a16_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a16_sint_description = { + PIPE_FORMAT_A16_SINT, + "PIPE_FORMAT_A16_SINT", + "a16_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i16_sint_description = { + PIPE_FORMAT_I16_SINT, + "PIPE_FORMAT_I16_SINT", + "i16_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16_sint_description = { + PIPE_FORMAT_L16_SINT, + "PIPE_FORMAT_L16_SINT", + "l16_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l16a16_sint_description = { + PIPE_FORMAT_L16A16_SINT, + "PIPE_FORMAT_L16A16_SINT", + "l16a16_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a32_uint_description = { + PIPE_FORMAT_A32_UINT, + "PIPE_FORMAT_A32_UINT", + "a32_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i32_uint_description = { + PIPE_FORMAT_I32_UINT, + "PIPE_FORMAT_I32_UINT", + "i32_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l32_uint_description = { + PIPE_FORMAT_L32_UINT, + "PIPE_FORMAT_L32_UINT", + "l32_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l32a32_uint_description = { + PIPE_FORMAT_L32A32_UINT, + "PIPE_FORMAT_L32A32_UINT", + "l32a32_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a32_sint_description = { + PIPE_FORMAT_A32_SINT, + "PIPE_FORMAT_A32_SINT", + "a32_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_0, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_i32_sint_description = { + PIPE_FORMAT_I32_SINT, + "PIPE_FORMAT_I32_SINT", + "i32_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = rgba */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l32_sint_description = { + PIPE_FORMAT_L32_SINT, + "PIPE_FORMAT_L32_SINT", + "l32_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 1, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_l32a32_sint_description = { + PIPE_FORMAT_L32A32_SINT, + "PIPE_FORMAT_L32A32_SINT", + "l32a32_sint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = rgb */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b10g10r10a2_uint_description = { + PIPE_FORMAT_B10G10R10A2_UINT, + "PIPE_FORMAT_B10G10R10A2_UINT", + "b10g10r10a2_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 20}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 20}, /* z = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8x8_snorm_description = { + PIPE_FORMAT_R8G8B8X8_SNORM, + "PIPE_FORMAT_R8G8B8X8_SNORM", + "r8g8b8x8_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8x8_srgb_description = { + PIPE_FORMAT_R8G8B8X8_SRGB, + "PIPE_FORMAT_R8G8B8X8_SRGB", + "r8g8b8x8_srgb", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_r8g8b8x8_uint_description = { + PIPE_FORMAT_R8G8B8X8_UINT, + "PIPE_FORMAT_R8G8B8X8_UINT", + "r8g8b8x8_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8g8b8x8_sint_description = { + PIPE_FORMAT_R8G8B8X8_SINT, + "PIPE_FORMAT_R8G8B8X8_SINT", + "r8g8b8x8_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 24}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b10g10r10x2_unorm_description = { + PIPE_FORMAT_B10G10R10X2_UNORM, + "PIPE_FORMAT_B10G10R10X2_UNORM", + "b10g10r10x2_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30}, /* x = x */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* y = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0} /* w = b */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 10, 20}, /* z = r */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 2, 30} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_W, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_X, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16x16_unorm_description = { + PIPE_FORMAT_R16G16B16X16_UNORM, + "PIPE_FORMAT_R16G16B16X16_UNORM", + "r16g16b16x16_unorm", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 48} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16x16_snorm_description = { + PIPE_FORMAT_R16G16B16X16_SNORM, + "PIPE_FORMAT_R16G16B16X16_SNORM", + "r16g16b16x16_snorm", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 48} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16x16_float_description = { + PIPE_FORMAT_R16G16B16X16_FLOAT, + "PIPE_FORMAT_R16G16B16X16_FLOAT", + "r16g16b16x16_float", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 48} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16x16_uint_description = { + PIPE_FORMAT_R16G16B16X16_UINT, + "PIPE_FORMAT_R16G16B16X16_UINT", + "r16g16b16x16_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 48} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16g16b16x16_sint_description = { + PIPE_FORMAT_R16G16B16X16_SINT, + "PIPE_FORMAT_R16G16B16X16_SINT", + "r16g16b16x16_sint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 48}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 16, 48} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32x32_float_description = { + PIPE_FORMAT_R32G32B32X32_FLOAT, + "PIPE_FORMAT_R32G32B32X32_FLOAT", + "r32g32b32x32_float", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 96} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32x32_uint_description = { + PIPE_FORMAT_R32G32B32X32_UINT, + "PIPE_FORMAT_R32G32B32X32_UINT", + "r32g32b32x32_uint", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 96} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32g32b32x32_sint_description = { + PIPE_FORMAT_R32G32B32X32_SINT, + "PIPE_FORMAT_R32G32B32X32_SINT", + "r32g32b32x32_sint", + {1, 1, 128}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 96}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 64}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 0} /* w = x */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = g */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 64}, /* z = b */ + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 32, 96} /* w = x */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8a8_snorm_description = { + PIPE_FORMAT_R8A8_SNORM, + "PIPE_FORMAT_R8A8_SNORM", + "r8a8_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16a16_unorm_description = { + PIPE_FORMAT_R16A16_UNORM, + "PIPE_FORMAT_R16A16_UNORM", + "r16a16_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16a16_snorm_description = { + PIPE_FORMAT_R16A16_SNORM, + "PIPE_FORMAT_R16A16_SNORM", + "r16a16_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16a16_float_description = { + PIPE_FORMAT_R16A16_FLOAT, + "PIPE_FORMAT_R16A16_FLOAT", + "r16a16_float", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32a32_float_description = { + PIPE_FORMAT_R32A32_FLOAT, + "PIPE_FORMAT_R32A32_FLOAT", + "r32a32_float", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_FLOAT, FALSE, FALSE, 32, 32}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8a8_uint_description = { + PIPE_FORMAT_R8A8_UINT, + "PIPE_FORMAT_R8A8_UINT", + "r8a8_uint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r8a8_sint_description = { + PIPE_FORMAT_R8A8_SINT, + "PIPE_FORMAT_R8A8_SINT", + "r8a8_sint", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 8, 8}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16a16_uint_description = { + PIPE_FORMAT_R16A16_UINT, + "PIPE_FORMAT_R16A16_UINT", + "r16a16_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r16a16_sint_description = { + PIPE_FORMAT_R16A16_SINT, + "PIPE_FORMAT_R16A16_SINT", + "r16a16_sint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 16, 16}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32a32_uint_description = { + PIPE_FORMAT_R32A32_UINT, + "PIPE_FORMAT_R32A32_UINT", + "r32a32_uint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 32, 32}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r32a32_sint_description = { + PIPE_FORMAT_R32A32_SINT, + "PIPE_FORMAT_R32A32_SINT", + "r32a32_sint", + {1, 1, 64}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + FALSE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_SIGNED, FALSE, TRUE, 32, 32}, /* y = a */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_0, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_Y /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_r10g10b10a2_uint_description = { + PIPE_FORMAT_R10G10B10A2_UINT, + "PIPE_FORMAT_R10G10B10A2_UINT", + "r10g10b10a2_uint", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 2, 30}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 20}, /* y = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 10}, /* z = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 0}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 10}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 10, 20}, /* z = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, FALSE, TRUE, 2, 30} /* w = a */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_X, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Z, /* b */ + UTIL_FORMAT_SWIZZLE_W /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_b5g6r5_srgb_description = { + PIPE_FORMAT_B5G6R5_SRGB, + "PIPE_FORMAT_B5G6R5_SRGB", + "b5g6r5_srgb", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 3, /* nr_channels */ + FALSE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 11}, /* x = r */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 6, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0}, /* z = b */ + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 0}, /* x = b */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 6, 5}, /* y = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 5, 11}, /* z = r */ + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_X, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Z, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Z, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_X, /* sb */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_a8l8_unorm_description = { + PIPE_FORMAT_A8L8_UNORM, + "PIPE_FORMAT_A8L8_UNORM", + "a8l8_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8l8_snorm_description = { + PIPE_FORMAT_A8L8_SNORM, + "PIPE_FORMAT_A8L8_SNORM", + "a8l8_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8l8_srgb_description = { + PIPE_FORMAT_A8L8_SRGB, + "PIPE_FORMAT_A8L8_SRGB", + "a8l8_srgb", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Y, /* sb */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* sr */ + UTIL_FORMAT_SWIZZLE_Y, /* sg */ + UTIL_FORMAT_SWIZZLE_Y, /* sb */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_SRGB, +}; + +const struct util_format_description +util_format_a16l16_unorm_description = { + PIPE_FORMAT_A16L16_UNORM, + "PIPE_FORMAT_A16L16_UNORM", + "a16l16_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = rgb */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_Y, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_g8r8_unorm_description = { + PIPE_FORMAT_G8R8_UNORM, + "PIPE_FORMAT_G8R8_UNORM", + "g8r8_unorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* x = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 0}, /* x = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_g8r8_snorm_description = { + PIPE_FORMAT_G8R8_SNORM, + "PIPE_FORMAT_G8R8_SNORM", + "g8r8_snorm", + {1, 1, 16}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* x = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_g16r16_unorm_description = { + PIPE_FORMAT_G16R16_UNORM, + "PIPE_FORMAT_G16R16_UNORM", + "g16r16_unorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* x = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 0}, /* x = g */ + {UTIL_FORMAT_TYPE_UNSIGNED, TRUE, FALSE, 16, 16}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_g16r16_snorm_description = { + PIPE_FORMAT_G16R16_SNORM, + "PIPE_FORMAT_G16R16_SNORM", + "g16r16_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 2, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* x = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 0}, /* x = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 16, 16}, /* y = r */ + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_Y, /* r */ + UTIL_FORMAT_SWIZZLE_X, /* g */ + UTIL_FORMAT_SWIZZLE_0, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_a8b8g8r8_snorm_description = { + PIPE_FORMAT_A8B8G8R8_SNORM, + "PIPE_FORMAT_A8B8G8R8_SNORM", + "a8b8g8r8_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0}, /* x = a */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_X /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description +util_format_x8b8g8r8_snorm_description = { + PIPE_FORMAT_X8B8G8R8_SNORM, + "PIPE_FORMAT_X8B8G8R8_SNORM", + "x8b8g8r8_snorm", + {1, 1, 32}, /* block */ + UTIL_FORMAT_LAYOUT_PLAIN, + 4, /* nr_channels */ + TRUE, /* is_array */ + TRUE, /* is_bitmask */ + FALSE, /* is_mixed */ +#ifdef PIPE_ARCH_BIG_ENDIAN + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 24}, /* x = x */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 0} /* w = r */ + }, +#else + { + {UTIL_FORMAT_TYPE_VOID, FALSE, FALSE, 8, 0}, /* x = x */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 8}, /* y = b */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 16}, /* z = g */ + {UTIL_FORMAT_TYPE_SIGNED, TRUE, FALSE, 8, 24} /* w = r */ + }, +#endif +#ifdef PIPE_ARCH_BIG_ENDIAN + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#else + { + UTIL_FORMAT_SWIZZLE_W, /* r */ + UTIL_FORMAT_SWIZZLE_Z, /* g */ + UTIL_FORMAT_SWIZZLE_Y, /* b */ + UTIL_FORMAT_SWIZZLE_1 /* a */ + }, +#endif + UTIL_FORMAT_COLORSPACE_RGB, +}; + +const struct util_format_description * +util_format_description(enum pipe_format format) +{ + if (format >= PIPE_FORMAT_COUNT) { + return NULL; + } + + switch (format) { + case PIPE_FORMAT_NONE: + return &util_format_none_description; + case PIPE_FORMAT_B8G8R8A8_UNORM: + return &util_format_b8g8r8a8_unorm_description; + case PIPE_FORMAT_B8G8R8X8_UNORM: + return &util_format_b8g8r8x8_unorm_description; + case PIPE_FORMAT_A8R8G8B8_UNORM: + return &util_format_a8r8g8b8_unorm_description; + case PIPE_FORMAT_X8R8G8B8_UNORM: + return &util_format_x8r8g8b8_unorm_description; + case PIPE_FORMAT_A8B8G8R8_UNORM: + return &util_format_a8b8g8r8_unorm_description; + case PIPE_FORMAT_X8B8G8R8_UNORM: + return &util_format_x8b8g8r8_unorm_description; + case PIPE_FORMAT_R8G8B8X8_UNORM: + return &util_format_r8g8b8x8_unorm_description; + case PIPE_FORMAT_B5G5R5X1_UNORM: + return &util_format_b5g5r5x1_unorm_description; + case PIPE_FORMAT_B5G5R5A1_UNORM: + return &util_format_b5g5r5a1_unorm_description; + case PIPE_FORMAT_B4G4R4A4_UNORM: + return &util_format_b4g4r4a4_unorm_description; + case PIPE_FORMAT_B4G4R4X4_UNORM: + return &util_format_b4g4r4x4_unorm_description; + case PIPE_FORMAT_A4B4G4R4_UNORM: + return &util_format_a4b4g4r4_unorm_description; + case PIPE_FORMAT_B5G6R5_UNORM: + return &util_format_b5g6r5_unorm_description; + case PIPE_FORMAT_R10G10B10A2_UNORM: + return &util_format_r10g10b10a2_unorm_description; + case PIPE_FORMAT_R10G10B10X2_UNORM: + return &util_format_r10g10b10x2_unorm_description; + case PIPE_FORMAT_B10G10R10A2_UNORM: + return &util_format_b10g10r10a2_unorm_description; + case PIPE_FORMAT_B2G3R3_UNORM: + return &util_format_b2g3r3_unorm_description; + case PIPE_FORMAT_L8_UNORM: + return &util_format_l8_unorm_description; + case PIPE_FORMAT_A8_UNORM: + return &util_format_a8_unorm_description; + case PIPE_FORMAT_I8_UNORM: + return &util_format_i8_unorm_description; + case PIPE_FORMAT_L4A4_UNORM: + return &util_format_l4a4_unorm_description; + case PIPE_FORMAT_L8A8_UNORM: + return &util_format_l8a8_unorm_description; + case PIPE_FORMAT_L16_UNORM: + return &util_format_l16_unorm_description; + case PIPE_FORMAT_A16_UNORM: + return &util_format_a16_unorm_description; + case PIPE_FORMAT_I16_UNORM: + return &util_format_i16_unorm_description; + case PIPE_FORMAT_L16A16_UNORM: + return &util_format_l16a16_unorm_description; + case PIPE_FORMAT_A8_SNORM: + return &util_format_a8_snorm_description; + case PIPE_FORMAT_L8_SNORM: + return &util_format_l8_snorm_description; + case PIPE_FORMAT_L8A8_SNORM: + return &util_format_l8a8_snorm_description; + case PIPE_FORMAT_I8_SNORM: + return &util_format_i8_snorm_description; + case PIPE_FORMAT_A16_SNORM: + return &util_format_a16_snorm_description; + case PIPE_FORMAT_L16_SNORM: + return &util_format_l16_snorm_description; + case PIPE_FORMAT_L16A16_SNORM: + return &util_format_l16a16_snorm_description; + case PIPE_FORMAT_I16_SNORM: + return &util_format_i16_snorm_description; + case PIPE_FORMAT_A16_FLOAT: + return &util_format_a16_float_description; + case PIPE_FORMAT_L16_FLOAT: + return &util_format_l16_float_description; + case PIPE_FORMAT_L16A16_FLOAT: + return &util_format_l16a16_float_description; + case PIPE_FORMAT_I16_FLOAT: + return &util_format_i16_float_description; + case PIPE_FORMAT_A32_FLOAT: + return &util_format_a32_float_description; + case PIPE_FORMAT_L32_FLOAT: + return &util_format_l32_float_description; + case PIPE_FORMAT_L32A32_FLOAT: + return &util_format_l32a32_float_description; + case PIPE_FORMAT_I32_FLOAT: + return &util_format_i32_float_description; + case PIPE_FORMAT_L8_SRGB: + return &util_format_l8_srgb_description; + case PIPE_FORMAT_R8_SRGB: + return &util_format_r8_srgb_description; + case PIPE_FORMAT_L8A8_SRGB: + return &util_format_l8a8_srgb_description; + case PIPE_FORMAT_R8G8B8_SRGB: + return &util_format_r8g8b8_srgb_description; + case PIPE_FORMAT_R8G8B8A8_SRGB: + return &util_format_r8g8b8a8_srgb_description; + case PIPE_FORMAT_A8B8G8R8_SRGB: + return &util_format_a8b8g8r8_srgb_description; + case PIPE_FORMAT_X8B8G8R8_SRGB: + return &util_format_x8b8g8r8_srgb_description; + case PIPE_FORMAT_B8G8R8A8_SRGB: + return &util_format_b8g8r8a8_srgb_description; + case PIPE_FORMAT_B8G8R8X8_SRGB: + return &util_format_b8g8r8x8_srgb_description; + case PIPE_FORMAT_A8R8G8B8_SRGB: + return &util_format_a8r8g8b8_srgb_description; + case PIPE_FORMAT_X8R8G8B8_SRGB: + return &util_format_x8r8g8b8_srgb_description; + case PIPE_FORMAT_R8SG8SB8UX8U_NORM: + return &util_format_r8sg8sb8ux8u_norm_description; + case PIPE_FORMAT_R10SG10SB10SA2U_NORM: + return &util_format_r10sg10sb10sa2u_norm_description; + case PIPE_FORMAT_R5SG5SB6U_NORM: + return &util_format_r5sg5sb6u_norm_description; + case PIPE_FORMAT_S8_UINT: + return &util_format_s8_uint_description; + case PIPE_FORMAT_Z16_UNORM: + return &util_format_z16_unorm_description; + case PIPE_FORMAT_Z32_UNORM: + return &util_format_z32_unorm_description; + case PIPE_FORMAT_Z32_FLOAT: + return &util_format_z32_float_description; + case PIPE_FORMAT_Z24_UNORM_S8_UINT: + return &util_format_z24_unorm_s8_uint_description; + case PIPE_FORMAT_S8_UINT_Z24_UNORM: + return &util_format_s8_uint_z24_unorm_description; + case PIPE_FORMAT_X24S8_UINT: + return &util_format_x24s8_uint_description; + case PIPE_FORMAT_S8X24_UINT: + return &util_format_s8x24_uint_description; + case PIPE_FORMAT_Z24X8_UNORM: + return &util_format_z24x8_unorm_description; + case PIPE_FORMAT_X8Z24_UNORM: + return &util_format_x8z24_unorm_description; + case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: + return &util_format_z32_float_s8x24_uint_description; + case PIPE_FORMAT_X32_S8X24_UINT: + return &util_format_x32_s8x24_uint_description; + case PIPE_FORMAT_UYVY: + return &util_format_uyvy_description; + case PIPE_FORMAT_YUYV: + return &util_format_yuyv_description; + case PIPE_FORMAT_R8G8_B8G8_UNORM: + return &util_format_r8g8_b8g8_unorm_description; + case PIPE_FORMAT_G8R8_G8B8_UNORM: + return &util_format_g8r8_g8b8_unorm_description; + case PIPE_FORMAT_G8R8_B8R8_UNORM: + return &util_format_g8r8_b8r8_unorm_description; + case PIPE_FORMAT_R8G8_R8B8_UNORM: + return &util_format_r8g8_r8b8_unorm_description; + case PIPE_FORMAT_R11G11B10_FLOAT: + return &util_format_r11g11b10_float_description; + case PIPE_FORMAT_R9G9B9E5_FLOAT: + return &util_format_r9g9b9e5_float_description; + case PIPE_FORMAT_R1_UNORM: + return &util_format_r1_unorm_description; + case PIPE_FORMAT_R8G8Bx_SNORM: + return &util_format_r8g8bx_snorm_description; + case PIPE_FORMAT_DXT1_RGB: + return &util_format_dxt1_rgb_description; + case PIPE_FORMAT_DXT1_RGBA: + return &util_format_dxt1_rgba_description; + case PIPE_FORMAT_DXT3_RGBA: + return &util_format_dxt3_rgba_description; + case PIPE_FORMAT_DXT5_RGBA: + return &util_format_dxt5_rgba_description; + case PIPE_FORMAT_DXT1_SRGB: + return &util_format_dxt1_srgb_description; + case PIPE_FORMAT_DXT1_SRGBA: + return &util_format_dxt1_srgba_description; + case PIPE_FORMAT_DXT3_SRGBA: + return &util_format_dxt3_srgba_description; + case PIPE_FORMAT_DXT5_SRGBA: + return &util_format_dxt5_srgba_description; + case PIPE_FORMAT_RGTC1_UNORM: + return &util_format_rgtc1_unorm_description; + case PIPE_FORMAT_RGTC1_SNORM: + return &util_format_rgtc1_snorm_description; + case PIPE_FORMAT_RGTC2_UNORM: + return &util_format_rgtc2_unorm_description; + case PIPE_FORMAT_RGTC2_SNORM: + return &util_format_rgtc2_snorm_description; + case PIPE_FORMAT_LATC1_UNORM: + return &util_format_latc1_unorm_description; + case PIPE_FORMAT_LATC1_SNORM: + return &util_format_latc1_snorm_description; + case PIPE_FORMAT_LATC2_UNORM: + return &util_format_latc2_unorm_description; + case PIPE_FORMAT_LATC2_SNORM: + return &util_format_latc2_snorm_description; + case PIPE_FORMAT_ETC1_RGB8: + return &util_format_etc1_rgb8_description; + case PIPE_FORMAT_BPTC_RGBA_UNORM: + return &util_format_bptc_rgba_unorm_description; + case PIPE_FORMAT_BPTC_SRGBA: + return &util_format_bptc_srgba_description; + case PIPE_FORMAT_BPTC_RGB_FLOAT: + return &util_format_bptc_rgb_float_description; + case PIPE_FORMAT_BPTC_RGB_UFLOAT: + return &util_format_bptc_rgb_ufloat_description; + case PIPE_FORMAT_R64_FLOAT: + return &util_format_r64_float_description; + case PIPE_FORMAT_R64G64_FLOAT: + return &util_format_r64g64_float_description; + case PIPE_FORMAT_R64G64B64_FLOAT: + return &util_format_r64g64b64_float_description; + case PIPE_FORMAT_R64G64B64A64_FLOAT: + return &util_format_r64g64b64a64_float_description; + case PIPE_FORMAT_R32_FLOAT: + return &util_format_r32_float_description; + case PIPE_FORMAT_R32G32_FLOAT: + return &util_format_r32g32_float_description; + case PIPE_FORMAT_R32G32B32_FLOAT: + return &util_format_r32g32b32_float_description; + case PIPE_FORMAT_R32G32B32A32_FLOAT: + return &util_format_r32g32b32a32_float_description; + case PIPE_FORMAT_R32_UNORM: + return &util_format_r32_unorm_description; + case PIPE_FORMAT_R32G32_UNORM: + return &util_format_r32g32_unorm_description; + case PIPE_FORMAT_R32G32B32_UNORM: + return &util_format_r32g32b32_unorm_description; + case PIPE_FORMAT_R32G32B32A32_UNORM: + return &util_format_r32g32b32a32_unorm_description; + case PIPE_FORMAT_R32_USCALED: + return &util_format_r32_uscaled_description; + case PIPE_FORMAT_R32G32_USCALED: + return &util_format_r32g32_uscaled_description; + case PIPE_FORMAT_R32G32B32_USCALED: + return &util_format_r32g32b32_uscaled_description; + case PIPE_FORMAT_R32G32B32A32_USCALED: + return &util_format_r32g32b32a32_uscaled_description; + case PIPE_FORMAT_R32_SNORM: + return &util_format_r32_snorm_description; + case PIPE_FORMAT_R32G32_SNORM: + return &util_format_r32g32_snorm_description; + case PIPE_FORMAT_R32G32B32_SNORM: + return &util_format_r32g32b32_snorm_description; + case PIPE_FORMAT_R32G32B32A32_SNORM: + return &util_format_r32g32b32a32_snorm_description; + case PIPE_FORMAT_R32_SSCALED: + return &util_format_r32_sscaled_description; + case PIPE_FORMAT_R32G32_SSCALED: + return &util_format_r32g32_sscaled_description; + case PIPE_FORMAT_R32G32B32_SSCALED: + return &util_format_r32g32b32_sscaled_description; + case PIPE_FORMAT_R32G32B32A32_SSCALED: + return &util_format_r32g32b32a32_sscaled_description; + case PIPE_FORMAT_R16_FLOAT: + return &util_format_r16_float_description; + case PIPE_FORMAT_R16G16_FLOAT: + return &util_format_r16g16_float_description; + case PIPE_FORMAT_R16G16B16_FLOAT: + return &util_format_r16g16b16_float_description; + case PIPE_FORMAT_R16G16B16A16_FLOAT: + return &util_format_r16g16b16a16_float_description; + case PIPE_FORMAT_R16_UNORM: + return &util_format_r16_unorm_description; + case PIPE_FORMAT_R16G16_UNORM: + return &util_format_r16g16_unorm_description; + case PIPE_FORMAT_R16G16B16_UNORM: + return &util_format_r16g16b16_unorm_description; + case PIPE_FORMAT_R16G16B16A16_UNORM: + return &util_format_r16g16b16a16_unorm_description; + case PIPE_FORMAT_R16_USCALED: + return &util_format_r16_uscaled_description; + case PIPE_FORMAT_R16G16_USCALED: + return &util_format_r16g16_uscaled_description; + case PIPE_FORMAT_R16G16B16_USCALED: + return &util_format_r16g16b16_uscaled_description; + case PIPE_FORMAT_R16G16B16A16_USCALED: + return &util_format_r16g16b16a16_uscaled_description; + case PIPE_FORMAT_R16_SNORM: + return &util_format_r16_snorm_description; + case PIPE_FORMAT_R16G16_SNORM: + return &util_format_r16g16_snorm_description; + case PIPE_FORMAT_R16G16B16_SNORM: + return &util_format_r16g16b16_snorm_description; + case PIPE_FORMAT_R16G16B16A16_SNORM: + return &util_format_r16g16b16a16_snorm_description; + case PIPE_FORMAT_R16_SSCALED: + return &util_format_r16_sscaled_description; + case PIPE_FORMAT_R16G16_SSCALED: + return &util_format_r16g16_sscaled_description; + case PIPE_FORMAT_R16G16B16_SSCALED: + return &util_format_r16g16b16_sscaled_description; + case PIPE_FORMAT_R16G16B16A16_SSCALED: + return &util_format_r16g16b16a16_sscaled_description; + case PIPE_FORMAT_R8_UNORM: + return &util_format_r8_unorm_description; + case PIPE_FORMAT_R8G8_UNORM: + return &util_format_r8g8_unorm_description; + case PIPE_FORMAT_R8G8B8_UNORM: + return &util_format_r8g8b8_unorm_description; + case PIPE_FORMAT_R8G8B8A8_UNORM: + return &util_format_r8g8b8a8_unorm_description; + case PIPE_FORMAT_R8_USCALED: + return &util_format_r8_uscaled_description; + case PIPE_FORMAT_R8G8_USCALED: + return &util_format_r8g8_uscaled_description; + case PIPE_FORMAT_R8G8B8_USCALED: + return &util_format_r8g8b8_uscaled_description; + case PIPE_FORMAT_R8G8B8A8_USCALED: + return &util_format_r8g8b8a8_uscaled_description; + case PIPE_FORMAT_R8_SNORM: + return &util_format_r8_snorm_description; + case PIPE_FORMAT_R8G8_SNORM: + return &util_format_r8g8_snorm_description; + case PIPE_FORMAT_R8G8B8_SNORM: + return &util_format_r8g8b8_snorm_description; + case PIPE_FORMAT_R8G8B8A8_SNORM: + return &util_format_r8g8b8a8_snorm_description; + case PIPE_FORMAT_R8_SSCALED: + return &util_format_r8_sscaled_description; + case PIPE_FORMAT_R8G8_SSCALED: + return &util_format_r8g8_sscaled_description; + case PIPE_FORMAT_R8G8B8_SSCALED: + return &util_format_r8g8b8_sscaled_description; + case PIPE_FORMAT_R8G8B8A8_SSCALED: + return &util_format_r8g8b8a8_sscaled_description; + case PIPE_FORMAT_R32_FIXED: + return &util_format_r32_fixed_description; + case PIPE_FORMAT_R32G32_FIXED: + return &util_format_r32g32_fixed_description; + case PIPE_FORMAT_R32G32B32_FIXED: + return &util_format_r32g32b32_fixed_description; + case PIPE_FORMAT_R32G32B32A32_FIXED: + return &util_format_r32g32b32a32_fixed_description; + case PIPE_FORMAT_R10G10B10X2_USCALED: + return &util_format_r10g10b10x2_uscaled_description; + case PIPE_FORMAT_R10G10B10X2_SNORM: + return &util_format_r10g10b10x2_snorm_description; + case PIPE_FORMAT_YV12: + return &util_format_yv12_description; + case PIPE_FORMAT_YV16: + return &util_format_yv16_description; + case PIPE_FORMAT_IYUV: + return &util_format_iyuv_description; + case PIPE_FORMAT_NV12: + return &util_format_nv12_description; + case PIPE_FORMAT_NV21: + return &util_format_nv21_description; + case PIPE_FORMAT_A4R4_UNORM: + return &util_format_a4r4_unorm_description; + case PIPE_FORMAT_R4A4_UNORM: + return &util_format_r4a4_unorm_description; + case PIPE_FORMAT_R8A8_UNORM: + return &util_format_r8a8_unorm_description; + case PIPE_FORMAT_A8R8_UNORM: + return &util_format_a8r8_unorm_description; + case PIPE_FORMAT_R10G10B10A2_USCALED: + return &util_format_r10g10b10a2_uscaled_description; + case PIPE_FORMAT_R10G10B10A2_SSCALED: + return &util_format_r10g10b10a2_sscaled_description; + case PIPE_FORMAT_R10G10B10A2_SNORM: + return &util_format_r10g10b10a2_snorm_description; + case PIPE_FORMAT_B10G10R10A2_USCALED: + return &util_format_b10g10r10a2_uscaled_description; + case PIPE_FORMAT_B10G10R10A2_SSCALED: + return &util_format_b10g10r10a2_sscaled_description; + case PIPE_FORMAT_B10G10R10A2_SNORM: + return &util_format_b10g10r10a2_snorm_description; + case PIPE_FORMAT_R8_UINT: + return &util_format_r8_uint_description; + case PIPE_FORMAT_R8G8_UINT: + return &util_format_r8g8_uint_description; + case PIPE_FORMAT_R8G8B8_UINT: + return &util_format_r8g8b8_uint_description; + case PIPE_FORMAT_R8G8B8A8_UINT: + return &util_format_r8g8b8a8_uint_description; + case PIPE_FORMAT_R8_SINT: + return &util_format_r8_sint_description; + case PIPE_FORMAT_R8G8_SINT: + return &util_format_r8g8_sint_description; + case PIPE_FORMAT_R8G8B8_SINT: + return &util_format_r8g8b8_sint_description; + case PIPE_FORMAT_R8G8B8A8_SINT: + return &util_format_r8g8b8a8_sint_description; + case PIPE_FORMAT_R16_UINT: + return &util_format_r16_uint_description; + case PIPE_FORMAT_R16G16_UINT: + return &util_format_r16g16_uint_description; + case PIPE_FORMAT_R16G16B16_UINT: + return &util_format_r16g16b16_uint_description; + case PIPE_FORMAT_R16G16B16A16_UINT: + return &util_format_r16g16b16a16_uint_description; + case PIPE_FORMAT_R16_SINT: + return &util_format_r16_sint_description; + case PIPE_FORMAT_R16G16_SINT: + return &util_format_r16g16_sint_description; + case PIPE_FORMAT_R16G16B16_SINT: + return &util_format_r16g16b16_sint_description; + case PIPE_FORMAT_R16G16B16A16_SINT: + return &util_format_r16g16b16a16_sint_description; + case PIPE_FORMAT_R32_UINT: + return &util_format_r32_uint_description; + case PIPE_FORMAT_R32G32_UINT: + return &util_format_r32g32_uint_description; + case PIPE_FORMAT_R32G32B32_UINT: + return &util_format_r32g32b32_uint_description; + case PIPE_FORMAT_R32G32B32A32_UINT: + return &util_format_r32g32b32a32_uint_description; + case PIPE_FORMAT_R32_SINT: + return &util_format_r32_sint_description; + case PIPE_FORMAT_R32G32_SINT: + return &util_format_r32g32_sint_description; + case PIPE_FORMAT_R32G32B32_SINT: + return &util_format_r32g32b32_sint_description; + case PIPE_FORMAT_R32G32B32A32_SINT: + return &util_format_r32g32b32a32_sint_description; + case PIPE_FORMAT_A8_UINT: + return &util_format_a8_uint_description; + case PIPE_FORMAT_I8_UINT: + return &util_format_i8_uint_description; + case PIPE_FORMAT_L8_UINT: + return &util_format_l8_uint_description; + case PIPE_FORMAT_L8A8_UINT: + return &util_format_l8a8_uint_description; + case PIPE_FORMAT_A8_SINT: + return &util_format_a8_sint_description; + case PIPE_FORMAT_I8_SINT: + return &util_format_i8_sint_description; + case PIPE_FORMAT_L8_SINT: + return &util_format_l8_sint_description; + case PIPE_FORMAT_L8A8_SINT: + return &util_format_l8a8_sint_description; + case PIPE_FORMAT_A16_UINT: + return &util_format_a16_uint_description; + case PIPE_FORMAT_I16_UINT: + return &util_format_i16_uint_description; + case PIPE_FORMAT_L16_UINT: + return &util_format_l16_uint_description; + case PIPE_FORMAT_L16A16_UINT: + return &util_format_l16a16_uint_description; + case PIPE_FORMAT_A16_SINT: + return &util_format_a16_sint_description; + case PIPE_FORMAT_I16_SINT: + return &util_format_i16_sint_description; + case PIPE_FORMAT_L16_SINT: + return &util_format_l16_sint_description; + case PIPE_FORMAT_L16A16_SINT: + return &util_format_l16a16_sint_description; + case PIPE_FORMAT_A32_UINT: + return &util_format_a32_uint_description; + case PIPE_FORMAT_I32_UINT: + return &util_format_i32_uint_description; + case PIPE_FORMAT_L32_UINT: + return &util_format_l32_uint_description; + case PIPE_FORMAT_L32A32_UINT: + return &util_format_l32a32_uint_description; + case PIPE_FORMAT_A32_SINT: + return &util_format_a32_sint_description; + case PIPE_FORMAT_I32_SINT: + return &util_format_i32_sint_description; + case PIPE_FORMAT_L32_SINT: + return &util_format_l32_sint_description; + case PIPE_FORMAT_L32A32_SINT: + return &util_format_l32a32_sint_description; + case PIPE_FORMAT_B10G10R10A2_UINT: + return &util_format_b10g10r10a2_uint_description; + case PIPE_FORMAT_R8G8B8X8_SNORM: + return &util_format_r8g8b8x8_snorm_description; + case PIPE_FORMAT_R8G8B8X8_SRGB: + return &util_format_r8g8b8x8_srgb_description; + case PIPE_FORMAT_R8G8B8X8_UINT: + return &util_format_r8g8b8x8_uint_description; + case PIPE_FORMAT_R8G8B8X8_SINT: + return &util_format_r8g8b8x8_sint_description; + case PIPE_FORMAT_B10G10R10X2_UNORM: + return &util_format_b10g10r10x2_unorm_description; + case PIPE_FORMAT_R16G16B16X16_UNORM: + return &util_format_r16g16b16x16_unorm_description; + case PIPE_FORMAT_R16G16B16X16_SNORM: + return &util_format_r16g16b16x16_snorm_description; + case PIPE_FORMAT_R16G16B16X16_FLOAT: + return &util_format_r16g16b16x16_float_description; + case PIPE_FORMAT_R16G16B16X16_UINT: + return &util_format_r16g16b16x16_uint_description; + case PIPE_FORMAT_R16G16B16X16_SINT: + return &util_format_r16g16b16x16_sint_description; + case PIPE_FORMAT_R32G32B32X32_FLOAT: + return &util_format_r32g32b32x32_float_description; + case PIPE_FORMAT_R32G32B32X32_UINT: + return &util_format_r32g32b32x32_uint_description; + case PIPE_FORMAT_R32G32B32X32_SINT: + return &util_format_r32g32b32x32_sint_description; + case PIPE_FORMAT_R8A8_SNORM: + return &util_format_r8a8_snorm_description; + case PIPE_FORMAT_R16A16_UNORM: + return &util_format_r16a16_unorm_description; + case PIPE_FORMAT_R16A16_SNORM: + return &util_format_r16a16_snorm_description; + case PIPE_FORMAT_R16A16_FLOAT: + return &util_format_r16a16_float_description; + case PIPE_FORMAT_R32A32_FLOAT: + return &util_format_r32a32_float_description; + case PIPE_FORMAT_R8A8_UINT: + return &util_format_r8a8_uint_description; + case PIPE_FORMAT_R8A8_SINT: + return &util_format_r8a8_sint_description; + case PIPE_FORMAT_R16A16_UINT: + return &util_format_r16a16_uint_description; + case PIPE_FORMAT_R16A16_SINT: + return &util_format_r16a16_sint_description; + case PIPE_FORMAT_R32A32_UINT: + return &util_format_r32a32_uint_description; + case PIPE_FORMAT_R32A32_SINT: + return &util_format_r32a32_sint_description; + case PIPE_FORMAT_R10G10B10A2_UINT: + return &util_format_r10g10b10a2_uint_description; + case PIPE_FORMAT_B5G6R5_SRGB: + return &util_format_b5g6r5_srgb_description; + case PIPE_FORMAT_A8L8_UNORM: + return &util_format_a8l8_unorm_description; + case PIPE_FORMAT_A8L8_SNORM: + return &util_format_a8l8_snorm_description; + case PIPE_FORMAT_A8L8_SRGB: + return &util_format_a8l8_srgb_description; + case PIPE_FORMAT_A16L16_UNORM: + return &util_format_a16l16_unorm_description; + case PIPE_FORMAT_G8R8_UNORM: + return &util_format_g8r8_unorm_description; + case PIPE_FORMAT_G8R8_SNORM: + return &util_format_g8r8_snorm_description; + case PIPE_FORMAT_G16R16_UNORM: + return &util_format_g16r16_unorm_description; + case PIPE_FORMAT_G16R16_SNORM: + return &util_format_g16r16_snorm_description; + case PIPE_FORMAT_A8B8G8R8_SNORM: + return &util_format_a8b8g8r8_snorm_description; + case PIPE_FORMAT_X8B8G8R8_SNORM: + return &util_format_x8b8g8r8_snorm_description; + default: + return NULL; + } +} + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_table.py b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_table.py new file mode 100644 index 0000000..dec2883 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_format_table.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python + +from __future__ import print_function +CopyRight = ''' +/************************************************************************** + * + * Copyright 2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +''' + + +import sys + +from u_format_parse import * + + +def layout_map(layout): + return 'UTIL_FORMAT_LAYOUT_' + str(layout).upper() + + +def colorspace_map(colorspace): + return 'UTIL_FORMAT_COLORSPACE_' + str(colorspace).upper() + + +colorspace_channels_map = { + 'rgb': ['r', 'g', 'b', 'a'], + 'srgb': ['sr', 'sg', 'sb', 'a'], + 'zs': ['z', 's'], + 'yuv': ['y', 'u', 'v'], +} + + +type_map = { + VOID: "UTIL_FORMAT_TYPE_VOID", + UNSIGNED: "UTIL_FORMAT_TYPE_UNSIGNED", + SIGNED: "UTIL_FORMAT_TYPE_SIGNED", + FIXED: "UTIL_FORMAT_TYPE_FIXED", + FLOAT: "UTIL_FORMAT_TYPE_FLOAT", +} + + +def bool_map(value): + if value: + return "TRUE" + else: + return "FALSE" + + +swizzle_map = { + SWIZZLE_X: "UTIL_FORMAT_SWIZZLE_X", + SWIZZLE_Y: "UTIL_FORMAT_SWIZZLE_Y", + SWIZZLE_Z: "UTIL_FORMAT_SWIZZLE_Z", + SWIZZLE_W: "UTIL_FORMAT_SWIZZLE_W", + SWIZZLE_0: "UTIL_FORMAT_SWIZZLE_0", + SWIZZLE_1: "UTIL_FORMAT_SWIZZLE_1", + SWIZZLE_NONE: "UTIL_FORMAT_SWIZZLE_NONE", +} + + +def write_format_table(formats): + print('/* This file is autogenerated by u_format_table.py from u_format.csv. Do not edit directly. */') + print() + # This will print the copyright message on the top of this file + print(CopyRight.strip()) + print() + print('#include "pipe/p_compiler.h"') + print('#include "u_format.h"') + print('#include "u_half.h"') + print('#include "u_math.h"') + print() + + def do_channel_array(channels, swizzles): + print(" {") + for i in range(4): + channel = channels[i] + if i < 3: + sep = "," + else: + sep = "" + if channel.size: + print(" {%s, %s, %s, %u, %u}%s\t/* %s = %s */" % (type_map[channel.type], bool_map(channel.norm), bool_map(channel.pure), channel.size, channel.shift, sep, "xyzw"[i], channel.name)) + else: + print(" {0, 0, 0, 0, 0}%s" % (sep,)) + print(" },") + + def do_swizzle_array(channels, swizzles): + print(" {") + for i in range(4): + swizzle = swizzles[i] + if i < 3: + sep = "," + else: + sep = "" + try: + comment = colorspace_channels_map[format.colorspace][i] + except (KeyError, IndexError): + comment = 'ignored' + print(" %s%s\t/* %s */" % (swizzle_map[swizzle], sep, comment)) + print(" },") + + def print_channels(format, func): + if format.nr_channels() <= 1: + func(format.le_channels, format.le_swizzles) + else: + print('#ifdef PIPE_ARCH_BIG_ENDIAN') + func(format.be_channels, format.be_swizzles) + print('#else') + func(format.le_channels, format.le_swizzles) + print('#endif') + + for format in formats: + print('const struct util_format_description') + print('util_format_%s_description = {' % (format.short_name(),)) + print(" %s," % (format.name,)) + print(" \"%s\"," % (format.name,)) + print(" \"%s\"," % (format.short_name(),)) + print(" {%u, %u, %u},\t/* block */" % (format.block_width, format.block_height, format.block_size())) + print(" %s," % (layout_map(format.layout),)) + print(" %u,\t/* nr_channels */" % (format.nr_channels(),)) + print(" %s,\t/* is_array */" % (bool_map(format.is_array()),)) + print(" %s,\t/* is_bitmask */" % (bool_map(format.is_bitmask()),)) + print(" %s,\t/* is_mixed */" % (bool_map(format.is_mixed()),)) + print_channels(format, do_channel_array) + print_channels(format, do_swizzle_array) + print(" %s," % (colorspace_map(format.colorspace),)) + print("};") + print() + + print("const struct util_format_description *") + print("util_format_description(enum pipe_format format)") + print("{") + print(" if (format >= PIPE_FORMAT_COUNT) {") + print(" return NULL;") + print(" }") + print() + print(" switch (format) {") + for format in formats: + print(" case %s:" % format.name) + print(" return &util_format_%s_description;" % (format.short_name(),)) + print(" default:") + print(" return NULL;") + print(" }") + print("}") + print() + + +def main(): + + formats = [] + for arg in sys.argv[1:]: + formats.extend(parse(arg)) + write_format_table(formats) + + +if __name__ == '__main__': + main() diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_half.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_half.h new file mode 100644 index 0000000..d28fae3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_half.h @@ -0,0 +1,131 @@ +/************************************************************************** + * + * Copyright 2010 Luca Barbieri + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef U_HALF_H +#define U_HALF_H + +#include "pipe/p_compiler.h" +#include "util/u_math.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * References for float <-> half conversions + * + * http://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/ + * https://gist.github.com/2156668 + * https://gist.github.com/2144712 + */ + +static inline uint16_t +util_float_to_half(float f) +{ + uint32_t sign_mask = 0x80000000; + uint32_t round_mask = ~0xfff; + uint32_t f32inf = 0xff << 23; + uint32_t f16inf = 0x1f << 23; + uint32_t sign; + union fi magic; + union fi f32; + uint16_t f16; + + magic.ui = 0xf << 23; + + f32.f = f; + + /* Sign */ + sign = f32.ui & sign_mask; + f32.ui ^= sign; + + if (f32.ui == f32inf) { + /* Inf */ + f16 = 0x7c00; + } else if (f32.ui > f32inf) { + /* NaN */ + f16 = 0x7e00; + } else { + /* Number */ + f32.ui &= round_mask; + f32.f *= magic.f; + f32.ui -= round_mask; + + /* + * Clamp to max finite value if overflowed. + * OpenGL has completely undefined rounding behavior for float to + * half-float conversions, and this matches what is mandated for float + * to fp11/fp10, which recommend round-to-nearest-finite too. + * (d3d10 is deeply unhappy about flushing such values to infinity, and + * while it also mandates round-to-zero it doesn't care nearly as much + * about that.) + */ + if (f32.ui > f16inf) + f32.ui = f16inf - 1; + + f16 = f32.ui >> 13; + } + + /* Sign */ + f16 |= sign >> 16; + + return f16; +} + +static inline float +util_half_to_float(uint16_t f16) +{ + union fi infnan; + union fi magic; + union fi f32; + + infnan.ui = 0x8f << 23; + infnan.f = 65536.0f; + magic.ui = 0xef << 23; + + /* Exponent / Mantissa */ + f32.ui = (f16 & 0x7fff) << 13; + + /* Adjust */ + f32.f *= magic.f; + + /* Inf / NaN */ + if (f32.f >= infnan.f) + f32.ui |= 0xff << 23; + + /* Sign */ + f32.ui |= (f16 & 0x8000) << 16; + + return f32.f; +} + +#ifdef __cplusplus +} +#endif + +#endif /* U_HALF_H */ + diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_hash_table.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_hash_table.c new file mode 100644 index 0000000..3b1fa63 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_hash_table.c @@ -0,0 +1,299 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * General purpose hash table implementation. + * + * Just uses the cso_hash for now, but it might be better switch to a linear + * probing hash table implementation at some point -- as it is said they have + * better lookup and cache performance and it appears to be possible to write + * a lock-free implementation of such hash tables . + * + * @author José Fonseca + */ + + +#include "pipe/p_compiler.h" +#include "util/u_debug.h" + +#include "cso_cache/cso_hash.h" + +#include "util/u_memory.h" +#include "util/u_hash_table.h" + + +struct util_hash_table +{ + struct cso_hash *cso; + + /** Hash function */ + unsigned (*hash)(void *key); + + /** Compare two keys */ + int (*compare)(void *key1, void *key2); + + /** free value */ + void (*destroy)(void *value); +}; + + +struct util_hash_table_item +{ + void *key; + void *value; +}; + + +static inline struct util_hash_table_item * +util_hash_table_item(struct cso_hash_iter iter) +{ + return (struct util_hash_table_item *)cso_hash_iter_data(iter); +} + + +struct util_hash_table * +util_hash_table_create(unsigned (*hash)(void *key), + int (*compare)(void *key1, void *key2), + void (*destroy)(void *value)) +{ + struct util_hash_table *ht; + + ht = MALLOC_STRUCT(util_hash_table); + if(!ht) + return NULL; + + ht->cso = cso_hash_create(); + if(!ht->cso) { + FREE(ht); + return NULL; + } + + ht->hash = hash; + ht->compare = compare; + ht->destroy = destroy; + + return ht; +} + + +static inline struct cso_hash_iter +util_hash_table_find_iter(struct util_hash_table *ht, + void *key, + unsigned key_hash) +{ + struct cso_hash_iter iter; + struct util_hash_table_item *item; + + iter = cso_hash_find(ht->cso, key_hash); + while (!cso_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)cso_hash_iter_data(iter); + if (!ht->compare(item->key, key)) + break; + iter = cso_hash_iter_next(iter); + } + + return iter; +} + + +static inline struct util_hash_table_item * +util_hash_table_find_item(struct util_hash_table *ht, + void *key, + unsigned key_hash) +{ + struct cso_hash_iter iter; + struct util_hash_table_item *item; + + iter = cso_hash_find(ht->cso, key_hash); + while (!cso_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)cso_hash_iter_data(iter); + if (!ht->compare(item->key, key)) + return item; + iter = cso_hash_iter_next(iter); + } + + return NULL; +} + + +enum pipe_error +util_hash_table_set(struct util_hash_table *ht, + void *key, + void *value) +{ + unsigned key_hash; + struct util_hash_table_item *item; + struct cso_hash_iter iter; + + assert(ht); + if (!ht) + return PIPE_ERROR_BAD_INPUT; + + key_hash = ht->hash(key); + + item = util_hash_table_find_item(ht, key, key_hash); + if(item) { + ht->destroy(item->value); + item->value = value; + return PIPE_OK; + } + + item = MALLOC_STRUCT(util_hash_table_item); + if(!item) + return PIPE_ERROR_OUT_OF_MEMORY; + + item->key = key; + item->value = value; + + iter = cso_hash_insert(ht->cso, key_hash, item); + if(cso_hash_iter_is_null(iter)) { + FREE(item); + return PIPE_ERROR_OUT_OF_MEMORY; + } + + return PIPE_OK; +} + + +void * +util_hash_table_get(struct util_hash_table *ht, + void *key) +{ + unsigned key_hash; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return NULL; + + key_hash = ht->hash(key); + + item = util_hash_table_find_item(ht, key, key_hash); + if(!item) + return NULL; + + return item->value; +} + + +void +util_hash_table_remove(struct util_hash_table *ht, + void *key) +{ + unsigned key_hash; + struct cso_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + key_hash = ht->hash(key); + + iter = util_hash_table_find_iter(ht, key, key_hash); + if(cso_hash_iter_is_null(iter)) + return; + + item = util_hash_table_item(iter); + assert(item); + ht->destroy(item->value); + FREE(item); + + cso_hash_erase(ht->cso, iter); +} + + +void +util_hash_table_clear(struct util_hash_table *ht) +{ + struct cso_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + iter = cso_hash_first_node(ht->cso); + while (!cso_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)cso_hash_take(ht->cso, cso_hash_iter_key(iter)); + ht->destroy(item->value); + FREE(item); + iter = cso_hash_first_node(ht->cso); + } +} + + +enum pipe_error +util_hash_table_foreach(struct util_hash_table *ht, + enum pipe_error (*callback) + (void *key, void *value, void *data), + void *data) +{ + struct cso_hash_iter iter; + struct util_hash_table_item *item; + enum pipe_error result; + + assert(ht); + if (!ht) + return PIPE_ERROR_BAD_INPUT; + + iter = cso_hash_first_node(ht->cso); + while (!cso_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)cso_hash_iter_data(iter); + result = callback(item->key, item->value, data); + if(result != PIPE_OK) + return result; + iter = cso_hash_iter_next(iter); + } + + return PIPE_OK; +} + + +void +util_hash_table_destroy(struct util_hash_table *ht) +{ + struct cso_hash_iter iter; + struct util_hash_table_item *item; + + assert(ht); + if (!ht) + return; + + iter = cso_hash_first_node(ht->cso); + while (!cso_hash_iter_is_null(iter)) { + item = (struct util_hash_table_item *)cso_hash_iter_data(iter); + ht->destroy(item->value); + FREE(item); + iter = cso_hash_iter_next(iter); + } + + cso_hash_delete(ht->cso); + + FREE(ht); +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_hash_table.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_hash_table.h new file mode 100644 index 0000000..b4a887b --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_hash_table.h @@ -0,0 +1,97 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * General purpose hash table. + * + * @author José Fonseca + */ + +#ifndef U_HASH_TABLE_H_ +#define U_HASH_TABLE_H_ + + +#include "pipe/p_defines.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Generic purpose hash table. + */ +struct util_hash_table; + + +/** + * Create an hash table. + * + * @param hash hash function + * @param compare should return 0 for two equal keys. + */ +struct util_hash_table * +util_hash_table_create(unsigned (*hash)(void *key), + int (*compare)(void *key1, void *key2), + void (*destroy)(void *value)); + + +enum pipe_error +util_hash_table_set(struct util_hash_table *ht, + void *key, + void *value); + +void * +util_hash_table_get(struct util_hash_table *ht, + void *key); + + +void +util_hash_table_remove(struct util_hash_table *ht, + void *key); + + +void +util_hash_table_clear(struct util_hash_table *ht); + + +enum pipe_error +util_hash_table_foreach(struct util_hash_table *ht, + enum pipe_error (*callback) + (void *key, void *value, void *data), + void *data); + +void +util_hash_table_destroy(struct util_hash_table *ht); + + +#ifdef __cplusplus +} +#endif + +#endif /* U_HASH_TABLE_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_inlines.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_inlines.h new file mode 100644 index 0000000..5a27e28 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_inlines.h @@ -0,0 +1,644 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_INLINES_H +#define U_INLINES_H + +#include "pipe/p_context.h" +#include "pipe/p_defines.h" +#include "pipe/p_shader_tokens.h" +#include "pipe/p_state.h" +#include "pipe/p_screen.h" +#include "util/u_debug.h" +#include "util/u_debug_describe.h" +#include "util/u_debug_refcnt.h" +#include "util/u_atomic.h" +#include "util/u_box.h" +#include "util/u_math.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Reference counting helper functions. + */ + + +static inline void +pipe_reference_init(struct pipe_reference *reference, unsigned count) +{ + p_atomic_set(&reference->count, count); +} + +static inline boolean +pipe_is_referenced(struct pipe_reference *reference) +{ + return p_atomic_read(&reference->count) != 0; +} + +/** + * Update reference counting. + * The old thing pointed to, if any, will be unreferenced. + * Both 'ptr' and 'reference' may be NULL. + * \return TRUE if the object's refcount hits zero and should be destroyed. + */ +static inline boolean +pipe_reference_described(struct pipe_reference *ptr, + struct pipe_reference *reference, + debug_reference_descriptor get_desc) +{ + boolean destroy = FALSE; + + if(ptr != reference) { + /* bump the reference.count first */ + if (reference) { + assert(pipe_is_referenced(reference)); + p_atomic_inc(&reference->count); + debug_reference(reference, get_desc, 1); + } + + if (ptr) { + assert(pipe_is_referenced(ptr)); + if (p_atomic_dec_zero(&ptr->count)) { + destroy = TRUE; + } + debug_reference(ptr, get_desc, -1); + } + } + + return destroy; +} + +static inline boolean +pipe_reference(struct pipe_reference *ptr, struct pipe_reference *reference) +{ + return pipe_reference_described(ptr, reference, + (debug_reference_descriptor)debug_describe_reference); +} + +static inline void +pipe_surface_reference(struct pipe_surface **ptr, struct pipe_surface *surf) +{ + struct pipe_surface *old_surf = *ptr; + + if (pipe_reference_described(&(*ptr)->reference, &surf->reference, + (debug_reference_descriptor)debug_describe_surface)) + old_surf->context->surface_destroy(old_surf->context, old_surf); + *ptr = surf; +} + +/** + * Similar to pipe_surface_reference() but always set the pointer to NULL + * and pass in an explicit context. The explicit context avoids the problem + * of using a deleted context's surface_destroy() method when freeing a surface + * that's shared by multiple contexts. + */ +static inline void +pipe_surface_release(struct pipe_context *pipe, struct pipe_surface **ptr) +{ + if (pipe_reference_described(&(*ptr)->reference, NULL, + (debug_reference_descriptor)debug_describe_surface)) + pipe->surface_destroy(pipe, *ptr); + *ptr = NULL; +} + + +static inline void +pipe_resource_reference(struct pipe_resource **ptr, struct pipe_resource *tex) +{ + struct pipe_resource *old_tex = *ptr; + + if (pipe_reference_described(&(*ptr)->reference, &tex->reference, + (debug_reference_descriptor)debug_describe_resource)) + old_tex->screen->resource_destroy(old_tex->screen, old_tex); + *ptr = tex; +} + +static inline void +pipe_sampler_view_reference(struct pipe_sampler_view **ptr, struct pipe_sampler_view *view) +{ + struct pipe_sampler_view *old_view = *ptr; + + if (pipe_reference_described(&(*ptr)->reference, &view->reference, + (debug_reference_descriptor)debug_describe_sampler_view)) + old_view->context->sampler_view_destroy(old_view->context, old_view); + *ptr = view; +} + +/** + * Similar to pipe_sampler_view_reference() but always set the pointer to + * NULL and pass in an explicit context. Passing an explicit context is a + * work-around for fixing a dangling context pointer problem when textures + * are shared by multiple contexts. XXX fix this someday. + */ +static inline void +pipe_sampler_view_release(struct pipe_context *ctx, + struct pipe_sampler_view **ptr) +{ + struct pipe_sampler_view *old_view = *ptr; + if (*ptr && (*ptr)->context != ctx) { + debug_printf_once(("context mis-match in pipe_sampler_view_release()\n")); + } + if (pipe_reference_described(&(*ptr)->reference, NULL, + (debug_reference_descriptor)debug_describe_sampler_view)) { + ctx->sampler_view_destroy(ctx, old_view); + } + *ptr = NULL; +} + + +static inline void +pipe_so_target_reference(struct pipe_stream_output_target **ptr, + struct pipe_stream_output_target *target) +{ + struct pipe_stream_output_target *old = *ptr; + + if (pipe_reference_described(&(*ptr)->reference, &target->reference, + (debug_reference_descriptor)debug_describe_so_target)) + old->context->stream_output_target_destroy(old->context, old); + *ptr = target; +} + +static inline void +pipe_surface_reset(struct pipe_context *ctx, struct pipe_surface* ps, + struct pipe_resource *pt, unsigned level, unsigned layer) +{ + pipe_resource_reference(&ps->texture, pt); + ps->format = pt->format; + ps->width = u_minify(pt->width0, level); + ps->height = u_minify(pt->height0, level); + ps->u.tex.level = level; + ps->u.tex.first_layer = ps->u.tex.last_layer = layer; + ps->context = ctx; +} + +static inline void +pipe_surface_init(struct pipe_context *ctx, struct pipe_surface* ps, + struct pipe_resource *pt, unsigned level, unsigned layer) +{ + ps->texture = 0; + pipe_reference_init(&ps->reference, 1); + pipe_surface_reset(ctx, ps, pt, level, layer); +} + +/* Return true if the surfaces are equal. */ +static inline boolean +pipe_surface_equal(struct pipe_surface *s1, struct pipe_surface *s2) +{ + return s1->texture == s2->texture && + s1->format == s2->format && + (s1->texture->target != PIPE_BUFFER || + (s1->u.buf.first_element == s2->u.buf.first_element && + s1->u.buf.last_element == s2->u.buf.last_element)) && + (s1->texture->target == PIPE_BUFFER || + (s1->u.tex.level == s2->u.tex.level && + s1->u.tex.first_layer == s2->u.tex.first_layer && + s1->u.tex.last_layer == s2->u.tex.last_layer)); +} + +/* + * Convenience wrappers for screen buffer functions. + */ + + +/** + * Create a new resource. + * \param bind bitmask of PIPE_BIND_x flags + * \param usage bitmask of PIPE_USAGE_x flags + */ +static inline struct pipe_resource * +pipe_buffer_create( struct pipe_screen *screen, + unsigned bind, + unsigned usage, + unsigned size ) +{ + struct pipe_resource buffer; + memset(&buffer, 0, sizeof buffer); + buffer.target = PIPE_BUFFER; + buffer.format = PIPE_FORMAT_R8_UNORM; /* want TYPELESS or similar */ + buffer.bind = bind; + buffer.usage = usage; + buffer.flags = 0; + buffer.width0 = size; + buffer.height0 = 1; + buffer.depth0 = 1; + buffer.array_size = 1; + return screen->resource_create(screen, &buffer); +} + + +/** + * Map a range of a resource. + * \param offset start of region, in bytes + * \param length size of region, in bytes + * \param access bitmask of PIPE_TRANSFER_x flags + * \param transfer returns a transfer object + */ +static inline void * +pipe_buffer_map_range(struct pipe_context *pipe, + struct pipe_resource *buffer, + unsigned offset, + unsigned length, + unsigned access, + struct pipe_transfer **transfer) +{ + struct pipe_box box; + void *map; + + assert(offset < buffer->width0); + assert(offset + length <= buffer->width0); + assert(length); + + u_box_1d(offset, length, &box); + + map = pipe->transfer_map(pipe, buffer, 0, access, &box, transfer); + if (map == NULL) { + return NULL; + } + + return map; +} + + +/** + * Map whole resource. + * \param access bitmask of PIPE_TRANSFER_x flags + * \param transfer returns a transfer object + */ +static inline void * +pipe_buffer_map(struct pipe_context *pipe, + struct pipe_resource *buffer, + unsigned access, + struct pipe_transfer **transfer) +{ + return pipe_buffer_map_range(pipe, buffer, 0, buffer->width0, access, transfer); +} + + +static inline void +pipe_buffer_unmap(struct pipe_context *pipe, + struct pipe_transfer *transfer) +{ + pipe->transfer_unmap(pipe, transfer); +} + +static inline void +pipe_buffer_flush_mapped_range(struct pipe_context *pipe, + struct pipe_transfer *transfer, + unsigned offset, + unsigned length) +{ + struct pipe_box box; + int transfer_offset; + + assert(length); + assert(transfer->box.x <= (int) offset); + assert((int) (offset + length) <= transfer->box.x + transfer->box.width); + + /* Match old screen->buffer_flush_mapped_range() behaviour, where + * offset parameter is relative to the start of the buffer, not the + * mapped range. + */ + transfer_offset = offset - transfer->box.x; + + u_box_1d(transfer_offset, length, &box); + + pipe->transfer_flush_region(pipe, transfer, &box); +} + +static inline void +pipe_buffer_write(struct pipe_context *pipe, + struct pipe_resource *buf, + unsigned offset, + unsigned size, + const void *data) +{ + struct pipe_box box; + unsigned access = PIPE_TRANSFER_WRITE; + + if (offset == 0 && size == buf->width0) { + access |= PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE; + } else { + access |= PIPE_TRANSFER_DISCARD_RANGE; + } + + u_box_1d(offset, size, &box); + + pipe->transfer_inline_write( pipe, + buf, + 0, + access, + &box, + data, + size, + 0); +} + +/** + * Special case for writing non-overlapping ranges. + * + * We can avoid GPU/CPU synchronization when writing range that has never + * been written before. + */ +static inline void +pipe_buffer_write_nooverlap(struct pipe_context *pipe, + struct pipe_resource *buf, + unsigned offset, unsigned size, + const void *data) +{ + struct pipe_box box; + + u_box_1d(offset, size, &box); + + pipe->transfer_inline_write(pipe, + buf, + 0, + (PIPE_TRANSFER_WRITE | + PIPE_TRANSFER_UNSYNCHRONIZED), + &box, + data, + 0, 0); +} + + +/** + * Create a new resource and immediately put data into it + * \param bind bitmask of PIPE_BIND_x flags + * \param usage bitmask of PIPE_USAGE_x flags + */ +static inline struct pipe_resource * +pipe_buffer_create_with_data(struct pipe_context *pipe, + unsigned bind, + unsigned usage, + unsigned size, + const void *ptr) +{ + struct pipe_resource *res = pipe_buffer_create(pipe->screen, + bind, usage, size); + pipe_buffer_write_nooverlap(pipe, res, 0, size, ptr); + return res; +} + +static inline void +pipe_buffer_read(struct pipe_context *pipe, + struct pipe_resource *buf, + unsigned offset, + unsigned size, + void *data) +{ + struct pipe_transfer *src_transfer; + ubyte *map; + + map = (ubyte *) pipe_buffer_map_range(pipe, + buf, + offset, size, + PIPE_TRANSFER_READ, + &src_transfer); + if (!map) + return; + + memcpy(data, map, size); + pipe_buffer_unmap(pipe, src_transfer); +} + + +/** + * Map a resource for reading/writing. + * \param access bitmask of PIPE_TRANSFER_x flags + */ +static inline void * +pipe_transfer_map(struct pipe_context *context, + struct pipe_resource *resource, + unsigned level, unsigned layer, + unsigned access, + unsigned x, unsigned y, + unsigned w, unsigned h, + struct pipe_transfer **transfer) +{ + struct pipe_box box; + u_box_2d_zslice(x, y, layer, w, h, &box); + return context->transfer_map(context, + resource, + level, + access, + &box, transfer); +} + + +/** + * Map a 3D (texture) resource for reading/writing. + * \param access bitmask of PIPE_TRANSFER_x flags + */ +static inline void * +pipe_transfer_map_3d(struct pipe_context *context, + struct pipe_resource *resource, + unsigned level, + unsigned access, + unsigned x, unsigned y, unsigned z, + unsigned w, unsigned h, unsigned d, + struct pipe_transfer **transfer) +{ + struct pipe_box box; + u_box_3d(x, y, z, w, h, d, &box); + return context->transfer_map(context, + resource, + level, + access, + &box, transfer); +} + +static inline void +pipe_transfer_unmap( struct pipe_context *context, + struct pipe_transfer *transfer ) +{ + context->transfer_unmap( context, transfer ); +} + +static inline void +pipe_set_constant_buffer(struct pipe_context *pipe, uint shader, uint index, + struct pipe_resource *buf) +{ + if (buf) { + struct pipe_constant_buffer cb; + cb.buffer = buf; + cb.buffer_offset = 0; + cb.buffer_size = buf->width0; + cb.user_buffer = NULL; + pipe->set_constant_buffer(pipe, shader, index, &cb); + } else { + pipe->set_constant_buffer(pipe, shader, index, NULL); + } +} + + +/** + * Get the polygon offset enable/disable flag for the given polygon fill mode. + * \param fill_mode one of PIPE_POLYGON_MODE_POINT/LINE/FILL + */ +static inline boolean +util_get_offset(const struct pipe_rasterizer_state *templ, + unsigned fill_mode) +{ + switch(fill_mode) { + case PIPE_POLYGON_MODE_POINT: + return templ->offset_point; + case PIPE_POLYGON_MODE_LINE: + return templ->offset_line; + case PIPE_POLYGON_MODE_FILL: + return templ->offset_tri; + default: + assert(0); + return FALSE; + } +} + +static inline float +util_get_min_point_size(const struct pipe_rasterizer_state *state) +{ + /* The point size should be clamped to this value at the rasterizer stage. + */ + return !state->point_quad_rasterization && + !state->point_smooth && + !state->multisample ? 1.0f : 0.0f; +} + +static inline void +util_query_clear_result(union pipe_query_result *result, unsigned type) +{ + switch (type) { + case PIPE_QUERY_OCCLUSION_PREDICATE: + case PIPE_QUERY_SO_OVERFLOW_PREDICATE: + case PIPE_QUERY_GPU_FINISHED: + result->b = FALSE; + break; + case PIPE_QUERY_OCCLUSION_COUNTER: + case PIPE_QUERY_TIMESTAMP: + case PIPE_QUERY_TIME_ELAPSED: + case PIPE_QUERY_PRIMITIVES_GENERATED: + case PIPE_QUERY_PRIMITIVES_EMITTED: + result->u64 = 0; + break; + case PIPE_QUERY_SO_STATISTICS: + memset(&result->so_statistics, 0, sizeof(result->so_statistics)); + break; + case PIPE_QUERY_TIMESTAMP_DISJOINT: + memset(&result->timestamp_disjoint, 0, sizeof(result->timestamp_disjoint)); + break; + case PIPE_QUERY_PIPELINE_STATISTICS: + memset(&result->pipeline_statistics, 0, sizeof(result->pipeline_statistics)); + break; + default: + memset(result, 0, sizeof(*result)); + } +} + +/** Convert PIPE_TEXTURE_x to TGSI_TEXTURE_x */ +static inline unsigned +util_pipe_tex_to_tgsi_tex(enum pipe_texture_target pipe_tex_target, + unsigned nr_samples) +{ + switch (pipe_tex_target) { + case PIPE_TEXTURE_1D: + assert(nr_samples <= 1); + return TGSI_TEXTURE_1D; + + case PIPE_TEXTURE_2D: + return nr_samples > 1 ? TGSI_TEXTURE_2D_MSAA : TGSI_TEXTURE_2D; + + case PIPE_TEXTURE_RECT: + assert(nr_samples <= 1); + return TGSI_TEXTURE_RECT; + + case PIPE_TEXTURE_3D: + assert(nr_samples <= 1); + return TGSI_TEXTURE_3D; + + case PIPE_TEXTURE_CUBE: + assert(nr_samples <= 1); + return TGSI_TEXTURE_CUBE; + + case PIPE_TEXTURE_1D_ARRAY: + assert(nr_samples <= 1); + return TGSI_TEXTURE_1D_ARRAY; + + case PIPE_TEXTURE_2D_ARRAY: + return nr_samples > 1 ? TGSI_TEXTURE_2D_ARRAY_MSAA : + TGSI_TEXTURE_2D_ARRAY; + + case PIPE_TEXTURE_CUBE_ARRAY: + return TGSI_TEXTURE_CUBE_ARRAY; + + default: + assert(0 && "unexpected texture target"); + return TGSI_TEXTURE_UNKNOWN; + } +} + + +static inline void +util_copy_constant_buffer(struct pipe_constant_buffer *dst, + const struct pipe_constant_buffer *src) +{ + if (src) { + pipe_resource_reference(&dst->buffer, src->buffer); + dst->buffer_offset = src->buffer_offset; + dst->buffer_size = src->buffer_size; + dst->user_buffer = src->user_buffer; + } + else { + pipe_resource_reference(&dst->buffer, NULL); + dst->buffer_offset = 0; + dst->buffer_size = 0; + dst->user_buffer = NULL; + } +} + +static inline unsigned +util_max_layer(const struct pipe_resource *r, unsigned level) +{ + switch (r->target) { + case PIPE_TEXTURE_CUBE: + return 6 - 1; + case PIPE_TEXTURE_3D: + return u_minify(r->depth0, level) - 1; + case PIPE_TEXTURE_1D_ARRAY: + case PIPE_TEXTURE_2D_ARRAY: + case PIPE_TEXTURE_CUBE_ARRAY: + return r->array_size - 1; + default: + return 0; + } +} + +#ifdef __cplusplus +} +#endif + +#endif /* U_INLINES_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_math.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_math.c new file mode 100644 index 0000000..e574153 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_math.c @@ -0,0 +1,137 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + + +#include "pipe/p_config.h" +#include "util/u_math.h" +#include "util/u_cpu_detect.h" + +#if defined(PIPE_ARCH_SSE) +#include +/* This is defined in pmmintrin.h, but it can only be included when -msse3 is + * used, so just define it here to avoid further. */ +#define _MM_DENORMALS_ZERO_MASK 0x0040 +#endif + +#if 0 +/** 2^x, for x in [-1.0, 1.0) */ +float pow2_table[POW2_TABLE_SIZE]; + + +static void +init_pow2_table(void) +{ + int i; + for (i = 0; i < POW2_TABLE_SIZE; i++) + pow2_table[i] = (float) pow(2.0, (i - POW2_TABLE_OFFSET) / POW2_TABLE_SCALE); +} + + +/** log2(x), for x in [1.0, 2.0) */ +float log2_table[LOG2_TABLE_SIZE]; + + +static void +init_log2_table(void) +{ + unsigned i; + for (i = 0; i < LOG2_TABLE_SIZE; i++) + log2_table[i] = (float) log2(1.0 + i * (1.0 / LOG2_TABLE_SCALE)); +} +#endif + +/** + * One time init for math utilities. + */ +void +util_init_math(void) +{ + static boolean initialized = FALSE; + if (!initialized) { + // init_pow2_table(); + /* init_log2_table();*/ + initialized = TRUE; + } +} + +/** + * Fetches the contents of the fpstate (mxcsr on x86) register. + * + * On platforms without support for it just returns 0. + */ +unsigned +util_fpstate_get(void) +{ + unsigned mxcsr = 0; + +#if defined(PIPE_ARCH_SSE) + if (util_cpu_caps.has_sse) { + mxcsr = _mm_getcsr(); + } +#endif + + return mxcsr; +} + +/** + * Make sure that the fp treats the denormalized floating + * point numbers as zero. + * + * This is the behavior required by D3D10. OpenGL doesn't care. + */ +unsigned +util_fpstate_set_denorms_to_zero(unsigned current_mxcsr) +{ +#if defined(PIPE_ARCH_SSE) + if (util_cpu_caps.has_sse) { + /* Enable flush to zero mode */ + current_mxcsr |= _MM_FLUSH_ZERO_MASK; + if (util_cpu_caps.has_daz) { + /* Enable denormals are zero mode */ + current_mxcsr |= _MM_DENORMALS_ZERO_MASK; + } + util_fpstate_set(current_mxcsr); + } +#endif + return current_mxcsr; +} + +/** + * Set the state of the fpstate (mxcsr on x86) register. + * + * On platforms without support for it's a noop. + */ +void +util_fpstate_set(unsigned mxcsr) +{ +#if defined(PIPE_ARCH_SSE) + if (util_cpu_caps.has_sse) { + _mm_setcsr(mxcsr); + } +#endif +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_math.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_math.h new file mode 100644 index 0000000..dd8e549 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_math.h @@ -0,0 +1,913 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/** + * Math utilities and approximations for common math functions. + * Reduced precision is usually acceptable in shaders... + * + * "fast" is used in the names of functions which are low-precision, + * or at least lower-precision than the normal C lib functions. + */ + + +#ifndef U_MATH_H +#define U_MATH_H + + +#include "pipe/p_compiler.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include + +#ifdef PIPE_OS_UNIX +#include /* for ffs */ +#endif + + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 +#endif + + +#if defined(_MSC_VER) + +#if _MSC_VER < 1400 && !defined(__cplusplus) + +static inline float cosf( float f ) +{ + return (float) cos( (double) f ); +} + +static inline float sinf( float f ) +{ + return (float) sin( (double) f ); +} + +static inline float ceilf( float f ) +{ + return (float) ceil( (double) f ); +} + +static inline float floorf( float f ) +{ + return (float) floor( (double) f ); +} + +static inline float powf( float f, float g ) +{ + return (float) pow( (double) f, (double) g ); +} + +static inline float sqrtf( float f ) +{ + return (float) sqrt( (double) f ); +} + +static inline float fabsf( float f ) +{ + return (float) fabs( (double) f ); +} + +static inline float logf( float f ) +{ + return (float) log( (double) f ); +} + +#else +/* Work-around an extra semi-colon in VS 2005 logf definition */ +#ifdef logf +#undef logf +#define logf(x) ((float)log((double)(x))) +#endif /* logf */ + +#if _MSC_VER < 1800 +#define isfinite(x) _finite((double)(x)) +#define isnan(x) _isnan((double)(x)) +#endif /* _MSC_VER < 1800 */ +#endif /* _MSC_VER < 1400 && !defined(__cplusplus) */ + +#if _MSC_VER < 1800 +static inline double log2( double x ) +{ + const double invln2 = 1.442695041; + return log( x ) * invln2; +} + +static inline double +round(double x) +{ + return x >= 0.0 ? floor(x + 0.5) : ceil(x - 0.5); +} + +static inline float +roundf(float x) +{ + return x >= 0.0f ? floorf(x + 0.5f) : ceilf(x - 0.5f); +} +#endif + +#ifndef INFINITY +#define INFINITY (DBL_MAX + DBL_MAX) +#endif + +#ifndef NAN +#define NAN (INFINITY - INFINITY) +#endif + +#endif /* _MSC_VER */ + + +#if __STDC_VERSION__ < 199901L && (!defined(__cplusplus) || defined(_MSC_VER)) +static inline long int +lrint(double d) +{ + long int rounded = (long int)(d + 0.5); + + if (d - floor(d) == 0.5) { + if (rounded % 2 != 0) + rounded += (d > 0) ? -1 : 1; + } + + return rounded; +} + +static inline long int +lrintf(float f) +{ + long int rounded = (long int)(f + 0.5f); + + if (f - floorf(f) == 0.5f) { + if (rounded % 2 != 0) + rounded += (f > 0) ? -1 : 1; + } + + return rounded; +} + +static inline long long int +llrint(double d) +{ + long long int rounded = (long long int)(d + 0.5); + + if (d - floor(d) == 0.5) { + if (rounded % 2 != 0) + rounded += (d > 0) ? -1 : 1; + } + + return rounded; +} + +static inline long long int +llrintf(float f) +{ + long long int rounded = (long long int)(f + 0.5f); + + if (f - floorf(f) == 0.5f) { + if (rounded % 2 != 0) + rounded += (f > 0) ? -1 : 1; + } + + return rounded; +} +#endif /* C99 */ + +#define POW2_TABLE_SIZE_LOG2 9 +#define POW2_TABLE_SIZE (1 << POW2_TABLE_SIZE_LOG2) +#define POW2_TABLE_OFFSET (POW2_TABLE_SIZE/2) +#define POW2_TABLE_SCALE ((float)(POW2_TABLE_SIZE/2)) +extern float pow2_table[POW2_TABLE_SIZE]; + + +/** + * Initialize math module. This should be called before using any + * other functions in this module. + */ +extern void +util_init_math(void); + + +union fi { + float f; + int32_t i; + uint32_t ui; +}; + + +union di { + double d; + int64_t i; + uint64_t ui; +}; + + +/** + * Extract the IEEE float32 exponent. + */ +static inline signed +util_get_float32_exponent(float x) { + union fi f; + + f.f = x; + + return ((f.ui >> 23) & 0xff) - 127; +} + + +/** + * Fast version of 2^x + * Identity: exp2(a + b) = exp2(a) * exp2(b) + * Let ipart = int(x) + * Let fpart = x - ipart; + * So, exp2(x) = exp2(ipart) * exp2(fpart) + * Compute exp2(ipart) with i << ipart + * Compute exp2(fpart) with lookup table. + */ +static inline float +util_fast_exp2(float x) +{ + int32_t ipart; + float fpart, mpart; + union fi epart; + + if(x > 129.00000f) + return 3.402823466e+38f; + + if (x < -126.99999f) + return 0.0f; + + ipart = (int32_t) x; + fpart = x - (float) ipart; + + /* same as + * epart.f = (float) (1 << ipart) + * but faster and without integer overflow for ipart > 31 + */ + epart.i = (ipart + 127 ) << 23; + + mpart = pow2_table[POW2_TABLE_OFFSET + (int)(fpart * POW2_TABLE_SCALE)]; + + return epart.f * mpart; +} + + +/** + * Fast approximation to exp(x). + */ +static inline float +util_fast_exp(float x) +{ + const float k = 1.44269f; /* = log2(e) */ + return util_fast_exp2(k * x); +} + + +#if 0 + +#define LOG2_TABLE_SIZE_LOG2 16 +#define LOG2_TABLE_SCALE (1 << LOG2_TABLE_SIZE_LOG2) +#define LOG2_TABLE_SIZE (LOG2_TABLE_SCALE + 1) +extern float log2_table[LOG2_TABLE_SIZE]; + + +/** + * Fast approximation to log2(x). + */ +static inline float +util_fast_log2(float x) +{ + union fi num; + float epart, mpart; + num.f = x; + epart = (float)(((num.i & 0x7f800000) >> 23) - 127); + /* mpart = log2_table[mantissa*LOG2_TABLE_SCALE + 0.5] */ + mpart = log2_table[((num.i & 0x007fffff) + (1 << (22 - LOG2_TABLE_SIZE_LOG2))) >> (23 - LOG2_TABLE_SIZE_LOG2)]; + return epart + mpart; +} + + +/** + * Fast approximation to x^y. + */ +static inline float +util_fast_pow(float x, float y) +{ + return util_fast_exp2(util_fast_log2(x) * y); +} +#endif +/* Note that this counts zero as a power of two. + */ +static inline boolean +util_is_power_of_two( unsigned v ) +{ + return (v & (v-1)) == 0; +} + + +/** + * Floor(x), returned as int. + */ +static inline int +util_ifloor(float f) +{ + int ai, bi; + double af, bf; + union fi u; + af = (3 << 22) + 0.5 + (double) f; + bf = (3 << 22) + 0.5 - (double) f; + u.f = (float) af; ai = u.i; + u.f = (float) bf; bi = u.i; + return (ai - bi) >> 1; +} + + +/** + * Round float to nearest int. + */ +static inline int +util_iround(float f) +{ +#if defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86) + int r; + __asm__ ("fistpl %0" : "=m" (r) : "t" (f) : "st"); + return r; +#elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86) + int r; + _asm { + fld f + fistp r + } + return r; +#else + if (f >= 0.0f) + return (int) (f + 0.5f); + else + return (int) (f - 0.5f); +#endif +} + + +/** + * Approximate floating point comparison + */ +static inline boolean +util_is_approx(float a, float b, float tol) +{ + return fabs(b - a) <= tol; +} + + +/** + * util_is_X_inf_or_nan = test if x is NaN or +/- Inf + * util_is_X_nan = test if x is NaN + * util_X_inf_sign = return +1 for +Inf, -1 for -Inf, or 0 for not Inf + * + * NaN can be checked with x != x, however this fails with the fast math flag + **/ + + +/** + * Single-float + */ +static inline boolean +util_is_inf_or_nan(float x) +{ + union fi tmp; + tmp.f = x; + return (tmp.ui & 0x7f800000) == 0x7f800000; +} + + +static inline boolean +util_is_nan(float x) +{ + union fi tmp; + tmp.f = x; + return (tmp.ui & 0x7fffffff) > 0x7f800000; +} + + +static inline int +util_inf_sign(float x) +{ + union fi tmp; + tmp.f = x; + if ((tmp.ui & 0x7fffffff) != 0x7f800000) { + return 0; + } + + return (x < 0) ? -1 : 1; +} + + +/** + * Double-float + */ +static inline boolean +util_is_double_inf_or_nan(double x) +{ + union di tmp; + tmp.d = x; + return (tmp.ui & 0x7ff0000000000000ULL) == 0x7ff0000000000000ULL; +} + + +static inline boolean +util_is_double_nan(double x) +{ + union di tmp; + tmp.d = x; + return (tmp.ui & 0x7fffffffffffffffULL) > 0x7ff0000000000000ULL; +} + + +static inline int +util_double_inf_sign(double x) +{ + union di tmp; + tmp.d = x; + if ((tmp.ui & 0x7fffffffffffffffULL) != 0x7ff0000000000000ULL) { + return 0; + } + + return (x < 0) ? -1 : 1; +} + + +/** + * Half-float + */ +static inline boolean +util_is_half_inf_or_nan(int16_t x) +{ + return (x & 0x7c00) == 0x7c00; +} + + +static inline boolean +util_is_half_nan(int16_t x) +{ + return (x & 0x7fff) > 0x7c00; +} + + +static inline int +util_half_inf_sign(int16_t x) +{ + if ((x & 0x7fff) != 0x7c00) { + return 0; + } + + return (x < 0) ? -1 : 1; +} + + +/** + * Find first bit set in word. Least significant bit is 1. + * Return 0 if no bits set. + */ +#ifndef FFS_DEFINED +#define FFS_DEFINED 1 + +#if defined(_MSC_VER) && _MSC_VER >= 1300 && (_M_IX86 || _M_AMD64 || _M_IA64) +unsigned char _BitScanForward(unsigned long* Index, unsigned long Mask); +#pragma intrinsic(_BitScanForward) +static inline +unsigned long ffs( unsigned long u ) +{ + unsigned long i; + if (_BitScanForward(&i, u)) + return i + 1; + else + return 0; +} +#elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86) +static inline +unsigned ffs( unsigned u ) +{ + unsigned i; + + if (u == 0) { + return 0; + } + + __asm bsf eax, [u] + __asm inc eax + __asm mov [i], eax + + return i; +} +#elif defined(__MINGW32__) || defined(PIPE_OS_ANDROID) +#define ffs __builtin_ffs +#endif + +#endif /* FFS_DEFINED */ + +/** + * Find last bit set in a word. The least significant bit is 1. + * Return 0 if no bits are set. + */ +static inline unsigned util_last_bit(unsigned u) +{ +#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 304) + return u == 0 ? 0 : 32 - __builtin_clz(u); +#else + unsigned r = 0; + while (u) { + r++; + u >>= 1; + } + return r; +#endif +} + +/** + * Find last bit in a word that does not match the sign bit. The least + * significant bit is 1. + * Return 0 if no bits are set. + */ +static inline unsigned util_last_bit_signed(int i) +{ +#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 407) + return 31 - __builtin_clrsb(i); +#else + if (i >= 0) + return util_last_bit(i); + else + return util_last_bit(~(unsigned)i); +#endif +} + +/* Destructively loop over all of the bits in a mask as in: + * + * while (mymask) { + * int i = u_bit_scan(&mymask); + * ... process element i + * } + * + */ +static inline int u_bit_scan(unsigned *mask) +{ + int i = ffs(*mask) - 1; + *mask &= ~(1 << i); + return i; +} + +/* For looping over a bitmask when you want to loop over consecutive bits + * manually, for example: + * + * while (mask) { + * int start, count, i; + * + * u_bit_scan_consecutive_range(&mask, &start, &count); + * + * for (i = 0; i < count; i++) + * ... process element (start+i) + * } + */ +static inline void +u_bit_scan_consecutive_range(unsigned *mask, int *start, int *count) +{ + if (*mask == 0xffffffff) { + *start = 0; + *count = 32; + *mask = 0; + return; + } + *start = ffs(*mask) - 1; + *count = ffs(~(*mask >> *start)) - 1; + *mask &= ~(((1u << *count) - 1) << *start); +} + +/** + * Return float bits. + */ +static inline unsigned +fui( float f ) +{ + union fi fi; + fi.f = f; + return fi.ui; +} + + +/** + * Convert ubyte to float in [0, 1]. + * XXX a 256-entry lookup table would be slightly faster. + */ +static inline float +ubyte_to_float(ubyte ub) +{ + return (float) ub * (1.0f / 255.0f); +} + + +/** + * Convert float in [0,1] to ubyte in [0,255] with clamping. + */ +static inline ubyte +float_to_ubyte(float f) +{ + union fi tmp; + + tmp.f = f; + if (tmp.i < 0) { + return (ubyte) 0; + } + else if (tmp.i >= 0x3f800000 /* 1.0f */) { + return (ubyte) 255; + } + else { + tmp.f = tmp.f * (255.0f/256.0f) + 32768.0f; + return (ubyte) tmp.i; + } +} + +static inline float +byte_to_float_tex(int8_t b) +{ + return (b == -128) ? -1.0F : b * 1.0F / 127.0F; +} + +static inline int8_t +float_to_byte_tex(float f) +{ + return (int8_t) (127.0F * f); +} + +/** + * Calc log base 2 + */ +static inline unsigned +util_logbase2(unsigned n) +{ +#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 304) + return ((sizeof(unsigned) * 8 - 1) - __builtin_clz(n | 1)); +#else + unsigned pos = 0; + if (n >= 1<<16) { n >>= 16; pos += 16; } + if (n >= 1<< 8) { n >>= 8; pos += 8; } + if (n >= 1<< 4) { n >>= 4; pos += 4; } + if (n >= 1<< 2) { n >>= 2; pos += 2; } + if (n >= 1<< 1) { pos += 1; } + return pos; +#endif +} + + +/** + * Returns the smallest power of two >= x + */ +static inline unsigned +util_next_power_of_two(unsigned x) +{ +#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 304) + if (x <= 1) + return 1; + + return (1 << ((sizeof(unsigned) * 8) - __builtin_clz(x - 1))); +#else + unsigned val = x; + + if (x <= 1) + return 1; + + if (util_is_power_of_two(x)) + return x; + + val--; + val = (val >> 1) | val; + val = (val >> 2) | val; + val = (val >> 4) | val; + val = (val >> 8) | val; + val = (val >> 16) | val; + val++; + return val; +#endif +} + + +/** + * Return number of bits set in n. + */ +static inline unsigned +util_bitcount(unsigned n) +{ +#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 304) + return __builtin_popcount(n); +#else + /* K&R classic bitcount. + * + * For each iteration, clear the LSB from the bitfield. + * Requires only one iteration per set bit, instead of + * one iteration per bit less than highest set bit. + */ + unsigned bits = 0; + for (bits; n; bits++) { + n &= n - 1; + } + return bits; +#endif +} + +/** + * Reverse bits in n + * Algorithm taken from: + * http://stackoverflow.com/questions/9144800/c-reverse-bits-in-unsigned-integer + */ +static inline unsigned +util_bitreverse(unsigned n) +{ + n = ((n >> 1) & 0x55555555u) | ((n & 0x55555555u) << 1); + n = ((n >> 2) & 0x33333333u) | ((n & 0x33333333u) << 2); + n = ((n >> 4) & 0x0f0f0f0fu) | ((n & 0x0f0f0f0fu) << 4); + n = ((n >> 8) & 0x00ff00ffu) | ((n & 0x00ff00ffu) << 8); + n = ((n >> 16) & 0xffffu) | ((n & 0xffffu) << 16); + return n; +} + +/** + * Convert from little endian to CPU byte order. + */ + +#ifdef PIPE_ARCH_BIG_ENDIAN +#define util_le64_to_cpu(x) util_bswap64(x) +#define util_le32_to_cpu(x) util_bswap32(x) +#define util_le16_to_cpu(x) util_bswap16(x) +#else +#define util_le64_to_cpu(x) (x) +#define util_le32_to_cpu(x) (x) +#define util_le16_to_cpu(x) (x) +#endif + +#define util_cpu_to_le64(x) util_le64_to_cpu(x) +#define util_cpu_to_le32(x) util_le32_to_cpu(x) +#define util_cpu_to_le16(x) util_le16_to_cpu(x) + +/** + * Reverse byte order of a 32 bit word. + */ +static inline uint32_t +util_bswap32(uint32_t n) +{ +/* We need the gcc version checks for non-autoconf build system */ +#if defined(HAVE___BUILTIN_BSWAP32) || (defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 403)) + return __builtin_bswap32(n); +#else + return (n >> 24) | + ((n >> 8) & 0x0000ff00) | + ((n << 8) & 0x00ff0000) | + (n << 24); +#endif +} + +/** + * Reverse byte order of a 64bit word. + */ +static inline uint64_t +util_bswap64(uint64_t n) +{ +#if defined(HAVE___BUILTIN_BSWAP64) + return __builtin_bswap64(n); +#else + return ((uint64_t)util_bswap32(n) << 32) | + util_bswap32((n >> 32)); +#endif +} + + +/** + * Reverse byte order of a 16 bit word. + */ +static inline uint16_t +util_bswap16(uint16_t n) +{ + return (n >> 8) | + (n << 8); +} + + +/** + * Clamp X to [MIN, MAX]. + * This is a macro to allow float, int, uint, etc. types. + */ +#define CLAMP( X, MIN, MAX ) ( (X)<(MIN) ? (MIN) : ((X)>(MAX) ? (MAX) : (X)) ) + +#define MIN2( A, B ) ( (A)<(B) ? (A) : (B) ) +#define MAX2( A, B ) ( (A)>(B) ? (A) : (B) ) + +#define MIN3( A, B, C ) ((A) < (B) ? MIN2(A, C) : MIN2(B, C)) +#define MAX3( A, B, C ) ((A) > (B) ? MAX2(A, C) : MAX2(B, C)) + +#define MIN4( A, B, C, D ) ((A) < (B) ? MIN3(A, C, D) : MIN3(B, C, D)) +#define MAX4( A, B, C, D ) ((A) > (B) ? MAX3(A, C, D) : MAX3(B, C, D)) + + +/** + * Align a value, only works pot alignemnts. + */ +static inline int +align(int value, int alignment) +{ + return (value + alignment - 1) & ~(alignment - 1); +} + +/** + * Works like align but on npot alignments. + */ +static inline size_t +util_align_npot(size_t value, size_t alignment) +{ + if (value % alignment) + return value + (alignment - (value % alignment)); + return value; +} + +static inline unsigned +u_minify(unsigned value, unsigned levels) +{ + return MAX2(1, value >> levels); +} + +#ifndef COPY_4V +#define COPY_4V( DST, SRC ) \ +do { \ + (DST)[0] = (SRC)[0]; \ + (DST)[1] = (SRC)[1]; \ + (DST)[2] = (SRC)[2]; \ + (DST)[3] = (SRC)[3]; \ +} while (0) +#endif + + +#ifndef COPY_4FV +#define COPY_4FV( DST, SRC ) COPY_4V(DST, SRC) +#endif + + +#ifndef ASSIGN_4V +#define ASSIGN_4V( DST, V0, V1, V2, V3 ) \ +do { \ + (DST)[0] = (V0); \ + (DST)[1] = (V1); \ + (DST)[2] = (V2); \ + (DST)[3] = (V3); \ +} while (0) +#endif + + +static inline uint32_t util_unsigned_fixed(float value, unsigned frac_bits) +{ + return value < 0 ? 0 : (uint32_t)(value * (1<MEMBER)) + + + +#ifdef __cplusplus +} +#endif + + +#endif /* U_MEMORY_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_pack_color.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_pack_color.h new file mode 100644 index 0000000..93e2d50 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_pack_color.h @@ -0,0 +1,87 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Functions to produce packed colors/Z from floats. + */ + + +#ifndef U_PACK_COLOR_H +#define U_PACK_COLOR_H + + +#include "pipe/p_compiler.h" +#include "pipe/p_format.h" +#include "util/u_debug.h" +#include "util/u_format.h" +#include "util/u_math.h" + + +/** + * Helper union for packing pixel values. + * Will often contain values in formats which are too complex to be described + * in simple terms, hence might just effectively contain a number of bytes. + * Must be big enough to hold data for all formats (currently 256 bits). + */ +union util_color { + ubyte ub; + ushort us; + uint ui[4]; + ushort h[4]; /* half float */ + float f[4]; + double d[4]; +}; + +/** + * Pack 4 ubytes into a 4-byte word + */ +static inline unsigned +pack_ub4(ubyte b0, ubyte b1, ubyte b2, ubyte b3) +{ + return ((((unsigned int)b0) << 0) | + (((unsigned int)b1) << 8) | + (((unsigned int)b2) << 16) | + (((unsigned int)b3) << 24)); +} + + +/** + * Pack/convert 4 floats into one 4-byte word. + */ +static inline unsigned +pack_ui32_float4(float a, float b, float c, float d) +{ + return pack_ub4( float_to_ubyte(a), + float_to_ubyte(b), + float_to_ubyte(c), + float_to_ubyte(d) ); +} + + + +#endif /* U_PACK_COLOR_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_pointer.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_pointer.h new file mode 100644 index 0000000..4f7a27c --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_pointer.h @@ -0,0 +1,130 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_POINTER_H +#define U_POINTER_H + +#include "pipe/p_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline intptr_t +pointer_to_intptr( const void *p ) +{ + union { + const void *p; + intptr_t i; + } pi; + pi.p = p; + return pi.i; +} + +static inline void * +intptr_to_pointer( intptr_t i ) +{ + union { + void *p; + intptr_t i; + } pi; + pi.i = i; + return pi.p; +} + +static inline uintptr_t +pointer_to_uintptr( const void *ptr ) +{ + union { + const void *p; + uintptr_t u; + } pu; + pu.p = ptr; + return pu.u; +} + +static inline void * +uintptr_to_pointer( uintptr_t u ) +{ + union { + void *p; + uintptr_t u; + } pu; + pu.u = u; + return pu.p; +} + +/** + * Return a pointer aligned to next multiple of N bytes. + */ +static inline void * +align_pointer( const void *unaligned, uintptr_t alignment ) +{ + uintptr_t aligned = (pointer_to_uintptr( unaligned ) + alignment - 1) & ~(alignment - 1); + return uintptr_to_pointer( aligned ); +} + + +/** + * Return a pointer aligned to next multiple of 16 bytes. + */ +static inline void * +align16( void *unaligned ) +{ + return align_pointer( unaligned, 16 ); +} + +typedef void (*func_pointer)(void); + +static inline func_pointer +pointer_to_func( void *p ) +{ + union { + void *p; + func_pointer f; + } pf; + pf.p = p; + return pf.f; +} + +static inline void * +func_to_pointer( func_pointer f ) +{ + union { + void *p; + func_pointer f; + } pf; + pf.f = f; + return pf.p; +} + + +#ifdef __cplusplus +} +#endif + +#endif /* U_POINTER_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_prim.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_prim.h new file mode 100644 index 0000000..2824af4 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_prim.h @@ -0,0 +1,268 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef U_PRIM_H +#define U_PRIM_H + + +#include "pipe/p_defines.h" +#include "util/u_debug.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct u_prim_vertex_count { + unsigned min; + unsigned incr; +}; + +/** + * Decompose a primitive that is a loop, a strip, or a fan. Return the + * original primitive if it is already decomposed. + */ +static inline unsigned +u_decomposed_prim(unsigned prim) +{ + switch (prim) { + case PIPE_PRIM_LINE_LOOP: + case PIPE_PRIM_LINE_STRIP: + return PIPE_PRIM_LINES; + case PIPE_PRIM_TRIANGLE_STRIP: + case PIPE_PRIM_TRIANGLE_FAN: + return PIPE_PRIM_TRIANGLES; + case PIPE_PRIM_QUAD_STRIP: + return PIPE_PRIM_QUADS; + case PIPE_PRIM_LINE_STRIP_ADJACENCY: + return PIPE_PRIM_LINES_ADJACENCY; + case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY: + return PIPE_PRIM_TRIANGLES_ADJACENCY; + default: + return prim; + } +} + +/** + * Reduce a primitive to one of PIPE_PRIM_POINTS, PIPE_PRIM_LINES, and + * PIPE_PRIM_TRIANGLES. + */ +static inline unsigned +u_reduced_prim(unsigned prim) +{ + switch (prim) { + case PIPE_PRIM_POINTS: + return PIPE_PRIM_POINTS; + case PIPE_PRIM_LINES: + case PIPE_PRIM_LINE_LOOP: + case PIPE_PRIM_LINE_STRIP: + case PIPE_PRIM_LINES_ADJACENCY: + case PIPE_PRIM_LINE_STRIP_ADJACENCY: + return PIPE_PRIM_LINES; + default: + return PIPE_PRIM_TRIANGLES; + } +} + +/** + * Re-assemble a primitive to remove its adjacency. + */ +static inline unsigned +u_assembled_prim(unsigned prim) +{ + switch (prim) { + case PIPE_PRIM_LINES_ADJACENCY: + case PIPE_PRIM_LINE_STRIP_ADJACENCY: + return PIPE_PRIM_LINES; + case PIPE_PRIM_TRIANGLES_ADJACENCY: + case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY: + return PIPE_PRIM_TRIANGLES; + default: + return prim; + } +} + +/** + * Return the vertex count information for a primitive. + * + * Note that if this function is called directly or indirectly anywhere in a + * source file, it will increase the size of the binary slightly more than + * expected because of the use of a table. + */ +static inline const struct u_prim_vertex_count * +u_prim_vertex_count(unsigned prim) +{ + static const struct u_prim_vertex_count prim_table[PIPE_PRIM_MAX] = { + { 1, 1 }, /* PIPE_PRIM_POINTS */ + { 2, 2 }, /* PIPE_PRIM_LINES */ + { 2, 1 }, /* PIPE_PRIM_LINE_LOOP */ + { 2, 1 }, /* PIPE_PRIM_LINE_STRIP */ + { 3, 3 }, /* PIPE_PRIM_TRIANGLES */ + { 3, 1 }, /* PIPE_PRIM_TRIANGLE_STRIP */ + { 3, 1 }, /* PIPE_PRIM_TRIANGLE_FAN */ + { 4, 4 }, /* PIPE_PRIM_QUADS */ + { 4, 2 }, /* PIPE_PRIM_QUAD_STRIP */ + { 3, 1 }, /* PIPE_PRIM_POLYGON */ + { 4, 4 }, /* PIPE_PRIM_LINES_ADJACENCY */ + { 4, 1 }, /* PIPE_PRIM_LINE_STRIP_ADJACENCY */ + { 6, 6 }, /* PIPE_PRIM_TRIANGLES_ADJACENCY */ + { 6, 2 }, /* PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY */ + }; + + return (likely(prim < PIPE_PRIM_MAX)) ? &prim_table[prim] : NULL; +} + +static inline boolean u_validate_pipe_prim( unsigned pipe_prim, unsigned nr ) +{ + const struct u_prim_vertex_count *count = u_prim_vertex_count(pipe_prim); + + return (count && nr >= count->min); +} + + +static inline boolean u_trim_pipe_prim( unsigned pipe_prim, unsigned *nr ) +{ + const struct u_prim_vertex_count *count = u_prim_vertex_count(pipe_prim); + + if (count && *nr >= count->min) { + if (count->incr > 1) + *nr -= (*nr % count->incr); + return TRUE; + } + else { + *nr = 0; + return FALSE; + } +} + +static inline unsigned +u_vertices_per_prim(int primitive) +{ + switch(primitive) { + case PIPE_PRIM_POINTS: + return 1; + case PIPE_PRIM_LINES: + case PIPE_PRIM_LINE_LOOP: + case PIPE_PRIM_LINE_STRIP: + return 2; + case PIPE_PRIM_TRIANGLES: + case PIPE_PRIM_TRIANGLE_STRIP: + case PIPE_PRIM_TRIANGLE_FAN: + return 3; + case PIPE_PRIM_LINES_ADJACENCY: + case PIPE_PRIM_LINE_STRIP_ADJACENCY: + return 4; + case PIPE_PRIM_TRIANGLES_ADJACENCY: + case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY: + return 6; + + /* following primitives should never be used + * with geometry shaders abd their size is + * undefined */ + case PIPE_PRIM_POLYGON: + case PIPE_PRIM_QUADS: + case PIPE_PRIM_QUAD_STRIP: + default: + debug_printf("Unrecognized geometry shader primitive"); + return 3; + } +} + +/** + * Returns the number of decomposed primitives for the given + * vertex count. + * Parts of the pipline are invoked once for each triangle in + * triangle strip, triangle fans and triangles and once + * for each line in line strip, line loop, lines. Also + * statistics depend on knowing the exact number of decomposed + * primitives for a set of vertices. + */ +static inline unsigned +u_decomposed_prims_for_vertices(int primitive, int vertices) +{ + switch (primitive) { + case PIPE_PRIM_POINTS: + return vertices; + case PIPE_PRIM_LINES: + return vertices / 2; + case PIPE_PRIM_LINE_LOOP: + return (vertices >= 2) ? vertices : 0; + case PIPE_PRIM_LINE_STRIP: + return (vertices >= 2) ? vertices - 1 : 0; + case PIPE_PRIM_TRIANGLES: + return vertices / 3; + case PIPE_PRIM_TRIANGLE_STRIP: + return (vertices >= 3) ? vertices - 2 : 0; + case PIPE_PRIM_TRIANGLE_FAN: + return (vertices >= 3) ? vertices - 2 : 0; + case PIPE_PRIM_LINES_ADJACENCY: + return vertices / 4; + case PIPE_PRIM_LINE_STRIP_ADJACENCY: + return (vertices >= 4) ? vertices - 3 : 0; + case PIPE_PRIM_TRIANGLES_ADJACENCY: + return vertices / 6; + case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY: + return (vertices >= 6) ? 1 + (vertices - 6) / 2 : 0; + case PIPE_PRIM_QUADS: + return vertices / 4; + case PIPE_PRIM_QUAD_STRIP: + return (vertices >= 4) ? (vertices - 2) / 2 : 0; + /* Polygons can't be decomposed + * because the number of their vertices isn't known so + * for them and whatever else we don't recognize just + * return 1 if the number of vertices is greater than + * or equal to 3 and zero otherwise */ + case PIPE_PRIM_POLYGON: + default: + debug_printf("Invalid decomposition primitive!\n"); + return (vertices >= 3) ? 1 : 0; + } +} + +/** + * Returns the number of reduced/tessellated primitives for the given vertex + * count. Each quad is treated as two triangles. Polygons are treated as + * triangle fans. + */ +static inline unsigned +u_reduced_prims_for_vertices(int primitive, int vertices) +{ + switch (primitive) { + case PIPE_PRIM_QUADS: + case PIPE_PRIM_QUAD_STRIP: + return u_decomposed_prims_for_vertices(primitive, vertices) * 2; + case PIPE_PRIM_POLYGON: + primitive = PIPE_PRIM_TRIANGLE_FAN; + /* fall through */ + default: + return u_decomposed_prims_for_vertices(primitive, vertices); + } +} + +const char *u_prim_name( unsigned pipe_prim ); + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_rect.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_rect.h new file mode 100644 index 0000000..b26f671 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_rect.h @@ -0,0 +1,104 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef U_RECT_H +#define U_RECT_H + +#include "pipe/p_compiler.h" +#include "util/u_math.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct u_rect { + int x0, x1; + int y0, y1; +}; + +/* Do two rectangles intersect? + */ +static inline boolean +u_rect_test_intersection(const struct u_rect *a, + const struct u_rect *b) +{ + return (!(a->x1 < b->x0 || + b->x1 < a->x0 || + a->y1 < b->y0 || + b->y1 < a->y0)); +} + +/* Find the intersection of two rectangles known to intersect. + */ +static inline void +u_rect_find_intersection(const struct u_rect *a, + struct u_rect *b) +{ + /* Caller should verify intersection exists before calling. + */ + if (b->x0 < a->x0) b->x0 = a->x0; + if (b->x1 > a->x1) b->x1 = a->x1; + if (b->y0 < a->y0) b->y0 = a->y0; + if (b->y1 > a->y1) b->y1 = a->y1; +} + + +static inline int +u_rect_area(const struct u_rect *r) +{ + return (r->x1 - r->x0) * (r->y1 - r->y0); +} + +static inline void +u_rect_possible_intersection(const struct u_rect *a, + struct u_rect *b) +{ + if (u_rect_test_intersection(a,b)) { + u_rect_find_intersection(a,b); + } + else { + b->x0 = b->x1 = b->y0 = b->y1 = 0; + } +} + +/* Set @d to a rectangle that covers both @a and @b. + */ +static inline void +u_rect_union(struct u_rect *d, const struct u_rect *a, const struct u_rect *b) +{ + d->x0 = MIN2(a->x0, b->x0); + d->y0 = MIN2(a->y0, b->y0); + d->x1 = MAX2(a->x1, b->x1); + d->y1 = MAX2(a->y1, b->y1); +} + +#ifdef __cplusplus +} +#endif + +#endif /* U_RECT_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_string.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_string.h new file mode 100644 index 0000000..da3e267 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_string.h @@ -0,0 +1,232 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Platform independent functions for string manipulation. + * + * @author Jose Fonseca + */ + +#ifndef U_STRING_H_ +#define U_STRING_H_ + +#if !defined(_MSC_VER) && !defined(XF86_LIBC_H) +#include +#endif +#include +#include + +#include "pipe/p_compiler.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _GNU_SOURCE + +#define util_strchrnul strchrnul + +#else + +static inline char * +util_strchrnul(const char *s, char c) +{ + for (; *s && *s != c; ++s); + + return (char *)s; +} + +#endif + +#ifdef _MSC_VER + +int util_vsnprintf(char *, size_t, const char *, va_list); +int util_snprintf(char *str, size_t size, const char *format, ...); + +static inline void +util_vsprintf(char *str, const char *format, va_list ap) +{ + util_vsnprintf(str, (size_t)-1, format, ap); +} + +static inline void +util_sprintf(char *str, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + util_vsnprintf(str, (size_t)-1, format, ap); + va_end(ap); +} + +static inline char * +util_strchr(const char *s, char c) +{ + char *p = util_strchrnul(s, c); + + return *p ? p : NULL; +} + +static inline char* +util_strncat(char *dst, const char *src, size_t n) +{ + char *p = dst + strlen(dst); + const char *q = src; + size_t i; + + for (i = 0; i < n && *q != '\0'; ++i) + *p++ = *q++; + *p = '\0'; + + return dst; +} + +static inline int +util_strcmp(const char *s1, const char *s2) +{ + unsigned char u1, u2; + + while (1) { + u1 = (unsigned char) *s1++; + u2 = (unsigned char) *s2++; + if (u1 != u2) + return u1 - u2; + if (u1 == '\0') + return 0; + } + return 0; +} + +static inline int +util_strncmp(const char *s1, const char *s2, size_t n) +{ + unsigned char u1, u2; + + while (n-- > 0) { + u1 = (unsigned char) *s1++; + u2 = (unsigned char) *s2++; + if (u1 != u2) + return u1 - u2; + if (u1 == '\0') + return 0; + } + return 0; +} + +static inline char * +util_strstr(const char *haystack, const char *needle) +{ + const char *p = haystack; + size_t len = strlen(needle); + + for (; (p = util_strchr(p, *needle)) != 0; p++) { + if (util_strncmp(p, needle, len) == 0) { + return (char *)p; + } + } + return NULL; +} + +static inline void * +util_memmove(void *dest, const void *src, size_t n) +{ + char *p = (char *)dest; + const char *q = (const char *)src; + if (dest < src) { + while (n--) + *p++ = *q++; + } + else + { + p += n; + q += n; + while (n--) + *--p = *--q; + } + return dest; +} + + +#else + +#define util_vsnprintf vsnprintf +#define util_snprintf snprintf +#define util_vsprintf vsprintf +#define util_sprintf sprintf +#define util_strchr strchr +#define util_strcmp strcmp +#define util_strncmp strncmp +#define util_strncat strncat +#define util_strstr strstr +#define util_memmove memmove + +#endif + + +/** + * Printable string buffer + */ +struct util_strbuf +{ + char *str; + char *ptr; + size_t left; +}; + + +static inline void +util_strbuf_init(struct util_strbuf *sbuf, char *str, size_t size) +{ + sbuf->str = str; + sbuf->str[0] = 0; + sbuf->ptr = sbuf->str; + sbuf->left = size; +} + + +static inline void +util_strbuf_printf(struct util_strbuf *sbuf, const char *format, ...) +{ + if(sbuf->left > 1) { + size_t written; + va_list ap; + va_start(ap, format); + written = util_vsnprintf(sbuf->ptr, sbuf->left, format, ap); + va_end(ap); + sbuf->ptr += written; + sbuf->left -= written; + } +} + + + +#ifdef __cplusplus +} +#endif + +#endif /* U_STRING_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_surface.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_surface.c new file mode 100644 index 0000000..ca27481 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_surface.c @@ -0,0 +1,462 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Surface utility functions. + * + * @author Brian Paul + */ + + +#include "pipe/p_defines.h" +#include "pipe/p_screen.h" +#include "pipe/p_state.h" + +#include "util/u_format.h" +#include "util/u_inlines.h" +#include "util/u_rect.h" +#include "util/u_surface.h" +#include "util/u_pack_color.h" + + +/** + * Initialize a pipe_surface object. 'view' is considered to have + * uninitialized contents. + */ +void +u_surface_default_template(struct pipe_surface *surf, + const struct pipe_resource *texture) +{ + memset(surf, 0, sizeof(*surf)); + + surf->format = texture->format; +} + + +/** + * Copy 2D rect from one place to another. + * Position and sizes are in pixels. + * src_stride may be negative to do vertical flip of pixels from source. + */ +void +util_copy_rect(ubyte * dst, + enum pipe_format format, + unsigned dst_stride, + unsigned dst_x, + unsigned dst_y, + unsigned width, + unsigned height, + const ubyte * src, + int src_stride, + unsigned src_x, + unsigned src_y) +{ + unsigned i; + int src_stride_pos = src_stride < 0 ? -src_stride : src_stride; + int blocksize = util_format_get_blocksize(format); + int blockwidth = util_format_get_blockwidth(format); + int blockheight = util_format_get_blockheight(format); + + assert(blocksize > 0); + assert(blockwidth > 0); + assert(blockheight > 0); + + dst_x /= blockwidth; + dst_y /= blockheight; + width = (width + blockwidth - 1)/blockwidth; + height = (height + blockheight - 1)/blockheight; + src_x /= blockwidth; + src_y /= blockheight; + + dst += dst_x * blocksize; + src += src_x * blocksize; + dst += dst_y * dst_stride; + src += src_y * src_stride_pos; + width *= blocksize; + + if (width == dst_stride && (int)width == src_stride) + memcpy(dst, src, height * width); + else { + for (i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += dst_stride; + src += src_stride; + } + } +} + + +/** + * Copy 3D box from one place to another. + * Position and sizes are in pixels. + */ +void +util_copy_box(ubyte * dst, + enum pipe_format format, + unsigned dst_stride, unsigned dst_slice_stride, + unsigned dst_x, unsigned dst_y, unsigned dst_z, + unsigned width, unsigned height, unsigned depth, + const ubyte * src, + int src_stride, unsigned src_slice_stride, + unsigned src_x, unsigned src_y, unsigned src_z) +{ + unsigned z; + dst += dst_z * dst_slice_stride; + src += src_z * src_slice_stride; + for (z = 0; z < depth; ++z) { + util_copy_rect(dst, + format, + dst_stride, + dst_x, dst_y, + width, height, + src, + src_stride, + src_x, src_y); + + dst += dst_slice_stride; + src += src_slice_stride; + } +} + + +void +util_fill_rect(ubyte * dst, + enum pipe_format format, + unsigned dst_stride, + unsigned dst_x, + unsigned dst_y, + unsigned width, + unsigned height, + union util_color *uc) +{ + const struct util_format_description *desc = util_format_description(format); + unsigned i, j; + unsigned width_size; + int blocksize = desc->block.bits / 8; + int blockwidth = desc->block.width; + int blockheight = desc->block.height; + + assert(blocksize > 0); + assert(blockwidth > 0); + assert(blockheight > 0); + + dst_x /= blockwidth; + dst_y /= blockheight; + width = (width + blockwidth - 1)/blockwidth; + height = (height + blockheight - 1)/blockheight; + + dst += dst_x * blocksize; + dst += dst_y * dst_stride; + width_size = width * blocksize; + + switch (blocksize) { + case 1: + if(dst_stride == width_size) + memset(dst, uc->ub, height * width_size); + else { + for (i = 0; i < height; i++) { + memset(dst, uc->ub, width_size); + dst += dst_stride; + } + } + break; + case 2: + for (i = 0; i < height; i++) { + uint16_t *row = (uint16_t *)dst; + for (j = 0; j < width; j++) + *row++ = uc->us; + dst += dst_stride; + } + break; + case 4: + for (i = 0; i < height; i++) { + uint32_t *row = (uint32_t *)dst; + for (j = 0; j < width; j++) + *row++ = uc->ui[0]; + dst += dst_stride; + } + break; + default: + for (i = 0; i < height; i++) { + ubyte *row = dst; + for (j = 0; j < width; j++) { + memcpy(row, uc, blocksize); + row += blocksize; + } + dst += dst_stride; + } + break; + } +} + + +void +util_fill_box(ubyte * dst, + enum pipe_format format, + unsigned stride, + unsigned layer_stride, + unsigned x, + unsigned y, + unsigned z, + unsigned width, + unsigned height, + unsigned depth, + union util_color *uc) +{ + unsigned layer; + dst += z * layer_stride; + for (layer = z; layer < depth; layer++) { + util_fill_rect(dst, format, + stride, + x, y, width, height, uc); + dst += layer_stride; + } +} + + +/** + * Fallback function for pipe->resource_copy_region(). + * Note: (X,Y)=(0,0) is always the upper-left corner. + */ +void +util_resource_copy_region(struct pipe_context *pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dst_x, unsigned dst_y, unsigned dst_z, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box) +{ + struct pipe_transfer *src_trans, *dst_trans; + uint8_t *dst_map; + const uint8_t *src_map; + MAYBE_UNUSED enum pipe_format src_format; + enum pipe_format dst_format; + struct pipe_box dst_box; + + assert(src && dst); + if (!src || !dst) + return; + + assert((src->target == PIPE_BUFFER && dst->target == PIPE_BUFFER) || + (src->target != PIPE_BUFFER && dst->target != PIPE_BUFFER)); + + src_format = src->format; + dst_format = dst->format; + + assert(util_format_get_blocksize(dst_format) == util_format_get_blocksize(src_format)); + assert(util_format_get_blockwidth(dst_format) == util_format_get_blockwidth(src_format)); + assert(util_format_get_blockheight(dst_format) == util_format_get_blockheight(src_format)); + + src_map = pipe->transfer_map(pipe, + src, + src_level, + PIPE_TRANSFER_READ, + src_box, &src_trans); + assert(src_map); + if (!src_map) { + goto no_src_map; + } + + dst_box.x = dst_x; + dst_box.y = dst_y; + dst_box.z = dst_z; + dst_box.width = src_box->width; + dst_box.height = src_box->height; + dst_box.depth = src_box->depth; + + dst_map = pipe->transfer_map(pipe, + dst, + dst_level, + PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD_RANGE, + &dst_box, &dst_trans); + assert(dst_map); + if (!dst_map) { + goto no_dst_map; + } + + if (dst->target == PIPE_BUFFER && src->target == PIPE_BUFFER) { + assert(src_box->height == 1); + assert(src_box->depth == 1); + memcpy(dst_map, src_map, src_box->width); + } else { + util_copy_box(dst_map, + dst_format, + dst_trans->stride, dst_trans->layer_stride, + 0, 0, 0, + src_box->width, src_box->height, src_box->depth, + src_map, + src_trans->stride, src_trans->layer_stride, + 0, 0, 0); + } + + pipe->transfer_unmap(pipe, dst_trans); +no_dst_map: + pipe->transfer_unmap(pipe, src_trans); +no_src_map: + ; +} + + + +#define UBYTE_TO_USHORT(B) ((B) | ((B) << 8)) + + +/* Return if the box is totally inside the resource. + */ +static boolean +is_box_inside_resource(const struct pipe_resource *res, + const struct pipe_box *box, + unsigned level) +{ + unsigned width = 1, height = 1, depth = 1; + + switch (res->target) { + case PIPE_BUFFER: + width = res->width0; + height = 1; + depth = 1; + break; + case PIPE_TEXTURE_1D: + width = u_minify(res->width0, level); + height = 1; + depth = 1; + break; + case PIPE_TEXTURE_2D: + case PIPE_TEXTURE_RECT: + width = u_minify(res->width0, level); + height = u_minify(res->height0, level); + depth = 1; + break; + case PIPE_TEXTURE_3D: + width = u_minify(res->width0, level); + height = u_minify(res->height0, level); + depth = u_minify(res->depth0, level); + break; + case PIPE_TEXTURE_CUBE: + width = u_minify(res->width0, level); + height = u_minify(res->height0, level); + depth = 6; + break; + case PIPE_TEXTURE_1D_ARRAY: + width = u_minify(res->width0, level); + height = 1; + depth = res->array_size; + break; + case PIPE_TEXTURE_2D_ARRAY: + width = u_minify(res->width0, level); + height = u_minify(res->height0, level); + depth = res->array_size; + break; + case PIPE_TEXTURE_CUBE_ARRAY: + width = u_minify(res->width0, level); + height = u_minify(res->height0, level); + depth = res->array_size; + assert(res->array_size % 6 == 0); + break; + case PIPE_MAX_TEXTURE_TYPES:; + } + + return box->x >= 0 && + box->x + box->width <= (int) width && + box->y >= 0 && + box->y + box->height <= (int) height && + box->z >= 0 && + box->z + box->depth <= (int) depth; +} + +static unsigned +get_sample_count(const struct pipe_resource *res) +{ + return res->nr_samples ? res->nr_samples : 1; +} + +/** + * Try to do a blit using resource_copy_region. The function calls + * resource_copy_region if the blit description is compatible with it. + * + * It returns TRUE if the blit was done using resource_copy_region. + * + * It returns FALSE otherwise and the caller must fall back to a more generic + * codepath for the blit operation. (e.g. by using u_blitter) + */ +boolean +util_try_blit_via_copy_region(struct pipe_context *ctx, + const struct pipe_blit_info *blit) +{ + unsigned mask = util_format_get_mask(blit->dst.format); + + /* No format conversions. */ + if (blit->src.resource->format != blit->src.format || + blit->dst.resource->format != blit->dst.format || + !util_is_format_compatible( + util_format_description(blit->src.resource->format), + util_format_description(blit->dst.resource->format))) { + return FALSE; + } + + /* No masks, no filtering, no scissor. */ + if ((blit->mask & mask) != mask || + blit->filter != PIPE_TEX_FILTER_NEAREST || + blit->scissor_enable) { + return FALSE; + } + + /* No flipping. */ + if (blit->src.box.width < 0 || + blit->src.box.height < 0 || + blit->src.box.depth < 0) { + return FALSE; + } + + /* No scaling. */ + if (blit->src.box.width != blit->dst.box.width || + blit->src.box.height != blit->dst.box.height || + blit->src.box.depth != blit->dst.box.depth) { + return FALSE; + } + + /* No out-of-bounds access. */ + if (!is_box_inside_resource(blit->src.resource, &blit->src.box, + blit->src.level) || + !is_box_inside_resource(blit->dst.resource, &blit->dst.box, + blit->dst.level)) { + return FALSE; + } + + /* Sample counts must match. */ + if (get_sample_count(blit->src.resource) != + get_sample_count(blit->dst.resource)) { + return FALSE; + } + + ctx->resource_copy_region(ctx, blit->dst.resource, blit->dst.level, + blit->dst.box.x, blit->dst.box.y, blit->dst.box.z, + blit->src.resource, blit->src.level, + &blit->src.box); + return TRUE; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_surface.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_surface.h new file mode 100644 index 0000000..bfd8f40 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_surface.h @@ -0,0 +1,111 @@ +/************************************************************************** + * + * Copyright 2009 VMware, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +#ifndef U_SURFACE_H +#define U_SURFACE_H + + +#include "pipe/p_compiler.h" +#include "pipe/p_state.h" + +#include "util/u_pack_color.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void +u_surface_default_template(struct pipe_surface *view, + const struct pipe_resource *texture); + +extern void +util_copy_rect(ubyte * dst, enum pipe_format format, + unsigned dst_stride, unsigned dst_x, unsigned dst_y, + unsigned width, unsigned height, const ubyte * src, + int src_stride, unsigned src_x, unsigned src_y); + +extern void +util_copy_box(ubyte * dst, + enum pipe_format format, + unsigned dst_stride, unsigned dst_slice_stride, + unsigned dst_x, unsigned dst_y, unsigned dst_z, + unsigned width, unsigned height, unsigned depth, + const ubyte * src, + int src_stride, unsigned src_slice_stride, + unsigned src_x, unsigned src_y, unsigned src_z); + +extern void +util_fill_rect(ubyte * dst, enum pipe_format format, + unsigned dst_stride, unsigned dst_x, unsigned dst_y, + unsigned width, unsigned height, union util_color *uc); + +extern void +util_fill_box(ubyte * dst, enum pipe_format format, + unsigned stride, unsigned layer_stride, + unsigned x, unsigned y, unsigned z, + unsigned width, unsigned height, unsigned depth, + union util_color *uc); + + +extern void +util_resource_copy_region(struct pipe_context *pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dst_x, unsigned dst_y, unsigned dst_z, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box); + +extern void +util_clear_render_target(struct pipe_context *pipe, + struct pipe_surface *dst, + const union pipe_color_union *color, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height); + +extern void +util_clear_depth_stencil(struct pipe_context *pipe, + struct pipe_surface *dst, + unsigned clear_flags, + double depth, + unsigned stencil, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height); + +extern boolean +util_try_blit_via_copy_region(struct pipe_context *ctx, + const struct pipe_blit_info *blit); + + +#ifdef __cplusplus +} +#endif + + +#endif /* U_SURFACE_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_texture.c b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_texture.c new file mode 100644 index 0000000..cf09368 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_texture.c @@ -0,0 +1,110 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * Copyright 2008 VMware, Inc. All rights reserved. + * Copyright 2009 Marek Olšák + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Texture mapping utility functions. + * + * @author Brian Paul + * Marek Olšák + */ + +#include "pipe/p_defines.h" + +#include "util/u_debug.h" +#include "util/u_texture.h" + +void util_map_texcoords2d_onto_cubemap(unsigned face, + const float *in_st, unsigned in_stride, + float *out_str, unsigned out_stride, + boolean allow_scale) +{ + int i; + float rx, ry, rz; + + /* loop over quad verts */ + for (i = 0; i < 4; i++) { + /* Compute sc = +/-scale and tc = +/-scale. + * Not +/-1 to avoid cube face selection ambiguity near the edges, + * though that can still sometimes happen with this scale factor... + * + * XXX: Yep, there is no safe scale factor that will prevent sampling + * the neighbouring face when stretching out. A more reliable solution + * would be to clamp (sc, tc) against +/- 1.0-1.0/mipsize, in the shader. + * + * Also, this is not necessary when minifying, or 1:1 blits. + */ + const float scale = allow_scale ? 0.9999f : 1.0f; + const float sc = (2 * in_st[0] - 1) * scale; + const float tc = (2 * in_st[1] - 1) * scale; + + switch (face) { + case PIPE_TEX_FACE_POS_X: + rx = 1; + ry = -tc; + rz = -sc; + break; + case PIPE_TEX_FACE_NEG_X: + rx = -1; + ry = -tc; + rz = sc; + break; + case PIPE_TEX_FACE_POS_Y: + rx = sc; + ry = 1; + rz = tc; + break; + case PIPE_TEX_FACE_NEG_Y: + rx = sc; + ry = -1; + rz = -tc; + break; + case PIPE_TEX_FACE_POS_Z: + rx = sc; + ry = -tc; + rz = 1; + break; + case PIPE_TEX_FACE_NEG_Z: + rx = -sc; + ry = -tc; + rz = -1; + break; + default: + rx = ry = rz = 0; + assert(0); + } + + out_str[0] = rx; /*s*/ + out_str[1] = ry; /*t*/ + out_str[2] = rz; /*r*/ + + in_st += in_stride; + out_str += out_stride; + } +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_texture.h b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_texture.h new file mode 100644 index 0000000..ea945c8 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/auxiliary/util/u_texture.h @@ -0,0 +1,57 @@ +/************************************************************************** + * + * Copyright 2009 Marek Olšák + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef U_TEXTURE_H +#define U_TEXTURE_H + +#include "pipe/p_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Convert 2D texture coordinates of 4 vertices into cubemap coordinates + * in the given face. + * Coordinates must be in the range [0,1]. + * + * \param face Cubemap face. + * \param in_st 4 pairs of 2D texture coordinates to convert. + * \param in_stride Stride of in_st in floats. + * \param out_str STR cubemap texture coordinates to compute. + * \param out_stride Stride of out_str in floats. + */ +void util_map_texcoords2d_onto_cubemap(unsigned face, + const float *in_st, unsigned in_stride, + float *out_str, unsigned out_stride, + boolean allow_scale); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads.h b/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads.h new file mode 100644 index 0000000..e7bbfb7 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads.h @@ -0,0 +1,71 @@ +/* + * C11 emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef EMULATED_THREADS_H_INCLUDED_ +#define EMULATED_THREADS_H_INCLUDED_ + +#include + +#ifndef TIME_UTC +#define TIME_UTC 1 +#endif + +#include "c99_compat.h" /* for `inline` */ + +/*---------------------------- types ----------------------------*/ +typedef void (*tss_dtor_t)(void*); +typedef int (*thrd_start_t)(void*); + +struct xtime { + time_t sec; + long nsec; +}; +typedef struct xtime xtime; + + +/*-------------------- enumeration constants --------------------*/ +enum { + mtx_plain = 0, + mtx_try = 1, + mtx_timed = 2, + mtx_recursive = 4 +}; + +enum { + thrd_success = 0, // succeeded + thrd_timeout, // timeout + thrd_error, // failed + thrd_busy, // resource busy + thrd_nomem // out of memory +}; + +/*-------------------------- functions --------------------------*/ + +#include "threads_posix.h" + +#endif /* EMULATED_THREADS_H_INCLUDED_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads_posix.h b/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads_posix.h new file mode 100644 index 0000000..ce9853b --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads_posix.h @@ -0,0 +1,374 @@ +/* + * C11 emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include +#ifndef assert +#include +#endif +#include +#include +#include +#include +#include /* for intptr_t */ + +/* +Configuration macro: + + EMULATED_THREADS_USE_NATIVE_TIMEDLOCK + Use pthread_mutex_timedlock() for `mtx_timedlock()' + Otherwise use mtx_trylock() + *busy loop* emulation. +*/ +#if !defined(__CYGWIN__) && !defined(__APPLE__) && !defined(__NetBSD__) +#define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK +#endif + + +#include + +/*---------------------------- macros ----------------------------*/ +#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT +#ifdef INIT_ONCE_STATIC_INIT +#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS +#else +#define TSS_DTOR_ITERATIONS 1 // assume TSS dtor MAY be called at least once. +#endif + +// FIXME: temporary non-standard hack to ease transition +#define _MTX_INITIALIZER_NP PTHREAD_MUTEX_INITIALIZER + +/*---------------------------- types ----------------------------*/ +typedef pthread_cond_t cnd_t; +typedef pthread_t thrd_t; +typedef pthread_key_t tss_t; +typedef pthread_mutex_t mtx_t; +typedef pthread_once_t once_flag; + + +/* +Implementation limits: + - Conditionally emulation for "mutex with timeout" + (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro) +*/ +struct impl_thrd_param { + thrd_start_t func; + void *arg; +}; + +static inline void * +impl_thrd_routine(void *p) +{ + struct impl_thrd_param pack = *((struct impl_thrd_param *)p); + free(p); + return (void*)(intptr_t)pack.func(pack.arg); +} + + +/*--------------- 7.25.2 Initialization functions ---------------*/ +// 7.25.2.1 +static inline void +call_once(once_flag *flag, void (*func)(void)) +{ + pthread_once(flag, func); +} + + +/*------------- 7.25.3 Condition variable functions -------------*/ +// 7.25.3.1 +static inline int +cnd_broadcast(cnd_t *cond) +{ + assert(cond != NULL); + return (pthread_cond_broadcast(cond) == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.2 +static inline void +cnd_destroy(cnd_t *cond) +{ + assert(cond); + pthread_cond_destroy(cond); +} + +// 7.25.3.3 +static inline int +cnd_init(cnd_t *cond) +{ + assert(cond != NULL); + return (pthread_cond_init(cond, NULL) == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.4 +static inline int +cnd_signal(cnd_t *cond) +{ + assert(cond != NULL); + return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.5 +static inline int +cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ + struct timespec abs_time; + int rt; + + assert(mtx != NULL); + assert(cond != NULL); + assert(xt != NULL); + + abs_time.tv_sec = xt->sec; + abs_time.tv_nsec = xt->nsec; + + rt = pthread_cond_timedwait(cond, mtx, &abs_time); + if (rt == ETIMEDOUT) + return thrd_busy; + return (rt == 0) ? thrd_success : thrd_error; +} + +// 7.25.3.6 +static inline int +cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + assert(mtx != NULL); + assert(cond != NULL); + return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success : thrd_error; +} + + +/*-------------------- 7.25.4 Mutex functions --------------------*/ +// 7.25.4.1 +static inline void +mtx_destroy(mtx_t *mtx) +{ + assert(mtx != NULL); + pthread_mutex_destroy(mtx); +} + +// 7.25.4.2 +static inline int +mtx_init(mtx_t *mtx, int type) +{ + pthread_mutexattr_t attr; + assert(mtx != NULL); + if (type != mtx_plain && type != mtx_timed && type != mtx_try + && type != (mtx_plain|mtx_recursive) + && type != (mtx_timed|mtx_recursive) + && type != (mtx_try|mtx_recursive)) + return thrd_error; + pthread_mutexattr_init(&attr); + if ((type & mtx_recursive) != 0) + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(mtx, &attr); + pthread_mutexattr_destroy(&attr); + return thrd_success; +} + +// 7.25.4.3 +static inline int +mtx_lock(mtx_t *mtx) +{ + assert(mtx != NULL); + return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error; +} + +static inline int +mtx_trylock(mtx_t *mtx); + +static inline void +thrd_yield(void); + +// 7.25.4.4 +static inline int +mtx_timedlock(mtx_t *mtx, const xtime *xt) +{ + assert(mtx != NULL); + assert(xt != NULL); + + { +#ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK + struct timespec ts; + int rt; + ts.tv_sec = xt->sec; + ts.tv_nsec = xt->nsec; + rt = pthread_mutex_timedlock(mtx, &ts); + if (rt == 0) + return thrd_success; + return (rt == ETIMEDOUT) ? thrd_busy : thrd_error; +#else + time_t expire = time(NULL); + expire += xt->sec; + while (mtx_trylock(mtx) != thrd_success) { + time_t now = time(NULL); + if (expire < now) + return thrd_busy; + // busy loop! + thrd_yield(); + } + return thrd_success; +#endif + } +} + +// 7.25.4.5 +static inline int +mtx_trylock(mtx_t *mtx) +{ + assert(mtx != NULL); + return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; +} + +// 7.25.4.6 +static inline int +mtx_unlock(mtx_t *mtx) +{ + assert(mtx != NULL); + return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error; +} + + +/*------------------- 7.25.5 Thread functions -------------------*/ +// 7.25.5.1 +static inline int +thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + struct impl_thrd_param *pack; + assert(thr != NULL); + pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); + if (!pack) return thrd_nomem; + pack->func = func; + pack->arg = arg; + if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) { + free(pack); + return thrd_error; + } + return thrd_success; +} + +// 7.25.5.2 +static inline thrd_t +thrd_current(void) +{ + return pthread_self(); +} + +// 7.25.5.3 +static inline int +thrd_detach(thrd_t thr) +{ + return (pthread_detach(thr) == 0) ? thrd_success : thrd_error; +} + +// 7.25.5.4 +static inline int +thrd_equal(thrd_t thr0, thrd_t thr1) +{ + return pthread_equal(thr0, thr1); +} + +// 7.25.5.5 +static inline void +thrd_exit(int res) +{ + pthread_exit((void*)(intptr_t)res); +} + +// 7.25.5.6 +static inline int +thrd_join(thrd_t thr, int *res) +{ + void *code; + if (pthread_join(thr, &code) != 0) + return thrd_error; + if (res) + *res = (int)(intptr_t)code; + return thrd_success; +} + +// 7.25.5.7 +static inline void +thrd_sleep(const xtime *xt) +{ + struct timespec req; + assert(xt); + req.tv_sec = xt->sec; + req.tv_nsec = xt->nsec; + nanosleep(&req, NULL); +} + +// 7.25.5.8 +static inline void +thrd_yield(void) +{ + sched_yield(); +} + + +/*----------- 7.25.6 Thread-specific storage functions -----------*/ +// 7.25.6.1 +static inline int +tss_create(tss_t *key, tss_dtor_t dtor) +{ + assert(key != NULL); + return (pthread_key_create(key, dtor) == 0) ? thrd_success : thrd_error; +} + +// 7.25.6.2 +static inline void +tss_delete(tss_t key) +{ + pthread_key_delete(key); +} + +// 7.25.6.3 +static inline void * +tss_get(tss_t key) +{ + return pthread_getspecific(key); +} + +// 7.25.6.4 +static inline int +tss_set(tss_t key, void *val) +{ + return (pthread_setspecific(key, val) == 0) ? thrd_success : thrd_error; +} + + +/*-------------------- 7.25.7 Time functions --------------------*/ +// 7.25.6.1 +static inline int +xtime_get(xtime *xt, int base) +{ + if (!xt) return 0; + if (base == TIME_UTC) { + xt->sec = time(NULL); + xt->nsec = 0; + return base; + } + return 0; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads_win32.h b/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads_win32.h new file mode 100644 index 0000000..d017c31 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/c11/threads_win32.h @@ -0,0 +1,642 @@ +/* + * C11 emulation library + * + * (C) Copyright yohhoy 2012. + * Distributed under the Boost Software License, Version 1.0. + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare [[derivative work]]s of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#ifndef assert +#include +#endif +#include +#include +#include // MSVCRT +#include + +/* +Configuration macro: + + EMULATED_THREADS_USE_NATIVE_CALL_ONCE + Use native WindowsAPI one-time initialization function. + (requires WinVista or later) + Otherwise emulate by mtx_trylock() + *busy loop* for WinXP. + + EMULATED_THREADS_USE_NATIVE_CV + Use native WindowsAPI condition variable object. + (requires WinVista or later) + Otherwise use emulated implementation for WinXP. + + EMULATED_THREADS_TSS_DTOR_SLOTNUM + Max registerable TSS dtor number. +*/ + +// XXX: Retain XP compatability +#if 0 +#if _WIN32_WINNT >= 0x0600 +// Prefer native WindowsAPI on newer environment. +#if !defined(__MINGW32__) +#define EMULATED_THREADS_USE_NATIVE_CALL_ONCE +#endif +#define EMULATED_THREADS_USE_NATIVE_CV +#endif +#endif +#define EMULATED_THREADS_TSS_DTOR_SLOTNUM 64 // see TLS_MINIMUM_AVAILABLE + + +#include + +// check configuration +#if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && (_WIN32_WINNT < 0x0600) +#error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600 +#endif + +#if defined(EMULATED_THREADS_USE_NATIVE_CV) && (_WIN32_WINNT < 0x0600) +#error EMULATED_THREADS_USE_NATIVE_CV requires _WIN32_WINNT>=0x0600 +#endif + + +/*---------------------------- macros ----------------------------*/ +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +#define ONCE_FLAG_INIT INIT_ONCE_STATIC_INIT +#else +#define ONCE_FLAG_INIT {0} +#endif +#define TSS_DTOR_ITERATIONS 1 + +// FIXME: temporary non-standard hack to ease transition +#define _MTX_INITIALIZER_NP {(PCRITICAL_SECTION_DEBUG)-1, -1, 0, 0, 0, 0} + +/*---------------------------- types ----------------------------*/ +typedef struct cnd_t { +#ifdef EMULATED_THREADS_USE_NATIVE_CV + CONDITION_VARIABLE condvar; +#else + int blocked; + int gone; + int to_unblock; + HANDLE sem_queue; + HANDLE sem_gate; + CRITICAL_SECTION monitor; +#endif +} cnd_t; + +typedef HANDLE thrd_t; + +typedef DWORD tss_t; + +typedef CRITICAL_SECTION mtx_t; + +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +typedef INIT_ONCE once_flag; +#else +typedef struct once_flag_t { + volatile LONG status; +} once_flag; +#endif + + +static inline void * tss_get(tss_t key); +static inline void thrd_yield(void); +static inline int mtx_trylock(mtx_t *mtx); +static inline int mtx_lock(mtx_t *mtx); +static inline int mtx_unlock(mtx_t *mtx); + +/* +Implementation limits: + - Conditionally emulation for "Initialization functions" + (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro) + - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop* +*/ +static void impl_tss_dtor_invoke(void); // forward decl. + +struct impl_thrd_param { + thrd_start_t func; + void *arg; +}; + +static unsigned __stdcall impl_thrd_routine(void *p) +{ + struct impl_thrd_param pack; + int code; + memcpy(&pack, p, sizeof(struct impl_thrd_param)); + free(p); + code = pack.func(pack.arg); + impl_tss_dtor_invoke(); + return (unsigned)code; +} + +static DWORD impl_xtime2msec(const xtime *xt) +{ + return (DWORD)((xt->sec * 1000U) + (xt->nsec / 1000000L)); +} + +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +struct impl_call_once_param { void (*func)(void); }; +static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context) +{ + struct impl_call_once_param *param = (struct impl_call_once_param*)Parameter; + (param->func)(); + ((void)InitOnce); ((void)Context); // suppress warning + return TRUE; +} +#endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE + +#ifndef EMULATED_THREADS_USE_NATIVE_CV +/* +Note: + The implementation of condition variable is ported from Boost.Interprocess + See http://www.boost.org/boost/interprocess/sync/windows/condition.hpp +*/ +static void impl_cond_do_signal(cnd_t *cond, int broadcast) +{ + int nsignal = 0; + + EnterCriticalSection(&cond->monitor); + if (cond->to_unblock != 0) { + if (cond->blocked == 0) { + LeaveCriticalSection(&cond->monitor); + return; + } + if (broadcast) { + cond->to_unblock += nsignal = cond->blocked; + cond->blocked = 0; + } else { + nsignal = 1; + cond->to_unblock++; + cond->blocked--; + } + } else if (cond->blocked > cond->gone) { + WaitForSingleObject(cond->sem_gate, INFINITE); + if (cond->gone != 0) { + cond->blocked -= cond->gone; + cond->gone = 0; + } + if (broadcast) { + nsignal = cond->to_unblock = cond->blocked; + cond->blocked = 0; + } else { + nsignal = cond->to_unblock = 1; + cond->blocked--; + } + } + LeaveCriticalSection(&cond->monitor); + + if (0 < nsignal) + ReleaseSemaphore(cond->sem_queue, nsignal, NULL); +} + +static int impl_cond_do_wait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ + int nleft = 0; + int ngone = 0; + int timeout = 0; + DWORD w; + + WaitForSingleObject(cond->sem_gate, INFINITE); + cond->blocked++; + ReleaseSemaphore(cond->sem_gate, 1, NULL); + + mtx_unlock(mtx); + + w = WaitForSingleObject(cond->sem_queue, xt ? impl_xtime2msec(xt) : INFINITE); + timeout = (w == WAIT_TIMEOUT); + + EnterCriticalSection(&cond->monitor); + if ((nleft = cond->to_unblock) != 0) { + if (timeout) { + if (cond->blocked != 0) { + cond->blocked--; + } else { + cond->gone++; + } + } + if (--cond->to_unblock == 0) { + if (cond->blocked != 0) { + ReleaseSemaphore(cond->sem_gate, 1, NULL); + nleft = 0; + } + else if ((ngone = cond->gone) != 0) { + cond->gone = 0; + } + } + } else if (++cond->gone == INT_MAX/2) { + WaitForSingleObject(cond->sem_gate, INFINITE); + cond->blocked -= cond->gone; + ReleaseSemaphore(cond->sem_gate, 1, NULL); + cond->gone = 0; + } + LeaveCriticalSection(&cond->monitor); + + if (nleft == 1) { + while (ngone--) + WaitForSingleObject(cond->sem_queue, INFINITE); + ReleaseSemaphore(cond->sem_gate, 1, NULL); + } + + mtx_lock(mtx); + return timeout ? thrd_busy : thrd_success; +} +#endif // ifndef EMULATED_THREADS_USE_NATIVE_CV + +static struct impl_tss_dtor_entry { + tss_t key; + tss_dtor_t dtor; +} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM]; + +static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) +{ + int i; + for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { + if (!impl_tss_dtor_tbl[i].dtor) + break; + } + if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM) + return 1; + impl_tss_dtor_tbl[i].key = key; + impl_tss_dtor_tbl[i].dtor = dtor; + return 0; +} + +static void impl_tss_dtor_invoke() +{ + int i; + for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) { + if (impl_tss_dtor_tbl[i].dtor) { + void* val = tss_get(impl_tss_dtor_tbl[i].key); + if (val) + (impl_tss_dtor_tbl[i].dtor)(val); + } + } +} + + +/*--------------- 7.25.2 Initialization functions ---------------*/ +// 7.25.2.1 +static inline void +call_once(once_flag *flag, void (*func)(void)) +{ + assert(flag && func); +#ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE + { + struct impl_call_once_param param; + param.func = func; + InitOnceExecuteOnce(flag, impl_call_once_callback, (PVOID)¶m, NULL); + } +#else + if (InterlockedCompareExchange(&flag->status, 1, 0) == 0) { + (func)(); + InterlockedExchange(&flag->status, 2); + } else { + while (flag->status == 1) { + // busy loop! + thrd_yield(); + } + } +#endif +} + + +/*------------- 7.25.3 Condition variable functions -------------*/ +// 7.25.3.1 +static inline int +cnd_broadcast(cnd_t *cond) +{ + if (!cond) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + WakeAllConditionVariable(&cond->condvar); +#else + impl_cond_do_signal(cond, 1); +#endif + return thrd_success; +} + +// 7.25.3.2 +static inline void +cnd_destroy(cnd_t *cond) +{ + assert(cond); +#ifdef EMULATED_THREADS_USE_NATIVE_CV + // do nothing +#else + CloseHandle(cond->sem_queue); + CloseHandle(cond->sem_gate); + DeleteCriticalSection(&cond->monitor); +#endif +} + +// 7.25.3.3 +static inline int +cnd_init(cnd_t *cond) +{ + if (!cond) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + InitializeConditionVariable(&cond->condvar); +#else + cond->blocked = 0; + cond->gone = 0; + cond->to_unblock = 0; + cond->sem_queue = CreateSemaphore(NULL, 0, LONG_MAX, NULL); + cond->sem_gate = CreateSemaphore(NULL, 1, 1, NULL); + InitializeCriticalSection(&cond->monitor); +#endif + return thrd_success; +} + +// 7.25.3.4 +static inline int +cnd_signal(cnd_t *cond) +{ + if (!cond) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + WakeConditionVariable(&cond->condvar); +#else + impl_cond_do_signal(cond, 0); +#endif + return thrd_success; +} + +// 7.25.3.5 +static inline int +cnd_timedwait(cnd_t *cond, mtx_t *mtx, const xtime *xt) +{ + if (!cond || !mtx || !xt) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + if (SleepConditionVariableCS(&cond->condvar, mtx, impl_xtime2msec(xt))) + return thrd_success; + return (GetLastError() == ERROR_TIMEOUT) ? thrd_busy : thrd_error; +#else + return impl_cond_do_wait(cond, mtx, xt); +#endif +} + +// 7.25.3.6 +static inline int +cnd_wait(cnd_t *cond, mtx_t *mtx) +{ + if (!cond || !mtx) return thrd_error; +#ifdef EMULATED_THREADS_USE_NATIVE_CV + SleepConditionVariableCS(&cond->condvar, mtx, INFINITE); +#else + impl_cond_do_wait(cond, mtx, NULL); +#endif + return thrd_success; +} + + +/*-------------------- 7.25.4 Mutex functions --------------------*/ +// 7.25.4.1 +static inline void +mtx_destroy(mtx_t *mtx) +{ + assert(mtx); + DeleteCriticalSection(mtx); +} + +// 7.25.4.2 +static inline int +mtx_init(mtx_t *mtx, int type) +{ + if (!mtx) return thrd_error; + if (type != mtx_plain && type != mtx_timed && type != mtx_try + && type != (mtx_plain|mtx_recursive) + && type != (mtx_timed|mtx_recursive) + && type != (mtx_try|mtx_recursive)) + return thrd_error; + InitializeCriticalSection(mtx); + return thrd_success; +} + +// 7.25.4.3 +static inline int +mtx_lock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + EnterCriticalSection(mtx); + return thrd_success; +} + +// 7.25.4.4 +static inline int +mtx_timedlock(mtx_t *mtx, const xtime *xt) +{ + time_t expire, now; + if (!mtx || !xt) return thrd_error; + expire = time(NULL); + expire += xt->sec; + while (mtx_trylock(mtx) != thrd_success) { + now = time(NULL); + if (expire < now) + return thrd_busy; + // busy loop! + thrd_yield(); + } + return thrd_success; +} + +// 7.25.4.5 +static inline int +mtx_trylock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + return TryEnterCriticalSection(mtx) ? thrd_success : thrd_busy; +} + +// 7.25.4.6 +static inline int +mtx_unlock(mtx_t *mtx) +{ + if (!mtx) return thrd_error; + LeaveCriticalSection(mtx); + return thrd_success; +} + + +/*------------------- 7.25.5 Thread functions -------------------*/ +// 7.25.5.1 +static inline int +thrd_create(thrd_t *thr, thrd_start_t func, void *arg) +{ + struct impl_thrd_param *pack; + uintptr_t handle; + if (!thr) return thrd_error; + pack = (struct impl_thrd_param *)malloc(sizeof(struct impl_thrd_param)); + if (!pack) return thrd_nomem; + pack->func = func; + pack->arg = arg; + handle = _beginthreadex(NULL, 0, impl_thrd_routine, pack, 0, NULL); + if (handle == 0) { + if (errno == EAGAIN || errno == EACCES) + return thrd_nomem; + return thrd_error; + } + *thr = (thrd_t)handle; + return thrd_success; +} + +#if 0 +// 7.25.5.2 +static inline thrd_t +thrd_current(void) +{ + HANDLE hCurrentThread; + BOOL bRet; + + /* GetCurrentThread() returns a pseudo-handle, which is useless. We need + * to call DuplicateHandle to get a real handle. However the handle value + * will not match the one returned by thread_create. + * + * Other potential solutions would be: + * - define thrd_t as a thread Ids, but this would mean we'd need to OpenThread for many operations + * - use malloc'ed memory for thrd_t. This would imply using TLS for current thread. + * + * Neither is particularly nice. + * + * Life would be much easier if C11 threads had different abstractions for + * threads and thread IDs, just like C++11 threads does... + */ + + bRet = DuplicateHandle(GetCurrentProcess(), // source process (pseudo) handle + GetCurrentThread(), // source (pseudo) handle + GetCurrentProcess(), // target process + &hCurrentThread, // target handle + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + assert(bRet); + if (!bRet) { + hCurrentThread = GetCurrentThread(); + } + return hCurrentThread; +} +#endif + +// 7.25.5.3 +static inline int +thrd_detach(thrd_t thr) +{ + CloseHandle(thr); + return thrd_success; +} + +// 7.25.5.4 +static inline int +thrd_equal(thrd_t thr0, thrd_t thr1) +{ + return GetThreadId(thr0) == GetThreadId(thr1); +} + +// 7.25.5.5 +static inline void +thrd_exit(int res) +{ + impl_tss_dtor_invoke(); + _endthreadex((unsigned)res); +} + +// 7.25.5.6 +static inline int +thrd_join(thrd_t thr, int *res) +{ + DWORD w, code; + w = WaitForSingleObject(thr, INFINITE); + if (w != WAIT_OBJECT_0) + return thrd_error; + if (res) { + if (!GetExitCodeThread(thr, &code)) { + CloseHandle(thr); + return thrd_error; + } + *res = (int)code; + } + CloseHandle(thr); + return thrd_success; +} + +// 7.25.5.7 +static inline void +thrd_sleep(const xtime *xt) +{ + assert(xt); + Sleep(impl_xtime2msec(xt)); +} + +// 7.25.5.8 +static inline void +thrd_yield(void) +{ + SwitchToThread(); +} + + +/*----------- 7.25.6 Thread-specific storage functions -----------*/ +// 7.25.6.1 +static inline int +tss_create(tss_t *key, tss_dtor_t dtor) +{ + if (!key) return thrd_error; + *key = TlsAlloc(); + if (dtor) { + if (impl_tss_dtor_register(*key, dtor)) { + TlsFree(*key); + return thrd_error; + } + } + return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error; +} + +// 7.25.6.2 +static inline void +tss_delete(tss_t key) +{ + TlsFree(key); +} + +// 7.25.6.3 +static inline void * +tss_get(tss_t key) +{ + return TlsGetValue(key); +} + +// 7.25.6.4 +static inline int +tss_set(tss_t key, void *val) +{ + return TlsSetValue(key, val) ? thrd_success : thrd_error; +} + + +/*-------------------- 7.25.7 Time functions --------------------*/ +// 7.25.6.1 +static inline int +xtime_get(xtime *xt, int base) +{ + if (!xt) return 0; + if (base == TIME_UTC) { + xt->sec = time(NULL); + xt->nsec = 0; + return base; + } + return 0; +} diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/c99_compat.h b/app/src/main/cpp/virglrenderer/src/gallium/include/c99_compat.h new file mode 100644 index 0000000..4be5b7e --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/c99_compat.h @@ -0,0 +1,138 @@ +/************************************************************************** + * + * Copyright 2007-2013 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "no_extern_c.h" + +#ifndef _C99_COMPAT_H_ +#define _C99_COMPAT_H_ + + +/* + * MSVC hacks. + */ +#if defined(_MSC_VER) + +# if _MSC_VER < 1500 +# error "Microsoft Visual Studio 2008 or higher required" +# endif + + /* + * Visual Studio 2012 will complain if we define the `inline` keyword, but + * actually it only supports the keyword on C++. + * + * To avoid this the _ALLOW_KEYWORD_MACROS must be set. + */ +# if (_MSC_VER >= 1700) && !defined(_ALLOW_KEYWORD_MACROS) +# define _ALLOW_KEYWORD_MACROS +# endif + + /* + * XXX: MSVC has a `__restrict` keyword, but it also has a + * `__declspec(restrict)` modifier, so it is impossible to define a + * `restrict` macro without interfering with the latter. Furthermore the + * MSVC standard library uses __declspec(restrict) under the _CRTRESTRICT + * macro. For now resolve this issue by redefining _CRTRESTRICT, but going + * forward we should probably should stop using restrict, especially + * considering that our code does not obbey strict aliasing rules any way. + */ +# include +# undef _CRTRESTRICT +# define _CRTRESTRICT +#endif + + +/* + * C99 inline keyword + */ +#ifndef inline +# ifdef __cplusplus + /* C++ supports inline keyword */ +# elif defined(__GNUC__) +# define inline __inline__ +# elif defined(_MSC_VER) +# define inline __inline +# elif defined(__ICL) +# define inline __inline +# elif defined(__INTEL_COMPILER) + /* Intel compiler supports inline keyword */ +# elif defined(__WATCOMC__) && (__WATCOMC__ >= 1100) +# define inline __inline +# elif (__STDC_VERSION__ >= 199901L) + /* C99 supports inline keyword */ +# else +# define inline +# endif +#endif + + +/* + * C99 restrict keyword + * + * See also: + * - http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html + */ +#ifndef restrict +# if (__STDC_VERSION__ >= 199901L) + /* C99 */ +# elif defined(__GNUC__) +# define restrict __restrict__ +# elif defined(_MSC_VER) +# define restrict __restrict +# else +# define restrict /* */ +# endif +#endif + + +/* + * C99 __func__ macro + */ +#ifndef __func__ +# if (__STDC_VERSION__ >= 199901L) + /* C99 */ +# elif defined(__GNUC__) +# define __func__ __FUNCTION__ +# elif defined(_MSC_VER) +# define __func__ __FUNCTION__ +# else +# define __func__ "" +# endif +#endif + + +/* Simple test case for debugging */ +#if 0 +static inline const char * +test_c99_compat_h(const void * restrict a, + const void * restrict b) +{ + return __func__; +} +#endif + + +#endif /* _C99_COMPAT_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/no_extern_c.h b/app/src/main/cpp/virglrenderer/src/gallium/include/no_extern_c.h new file mode 100644 index 0000000..f79602c --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/no_extern_c.h @@ -0,0 +1,48 @@ +/************************************************************************** + * + * Copyright 2014 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Including system's headers inside `extern "C" { ... }` is not safe, as system + * headers may have C++ code in them, and C++ code inside extern "C" + * leads to syntatically incorrect code. + * + * This is because putting code inside extern "C" won't make __cplusplus define + * go away, that is, the system header being included thinks is free to use C++ + * as it sees fits. + * + * Including non-system headers inside extern "C" is not safe either, because + * non-system headers end up including system headers, hence fall in the above + * case too. + * + * Conclusion, includes inside extern "C" is simply not portable. + * + * + * This header helps surface these issues. + */ + +#ifdef __cplusplus +template class _IncludeInsideExternCNotPortable; +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_compiler.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_compiler.h new file mode 100644 index 0000000..faba6d4 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_compiler.h @@ -0,0 +1,249 @@ +/************************************************************************** + * + * Copyright 2007-2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef P_COMPILER_H +#define P_COMPILER_H + + + +#include "p_config.h" + +#include +#include +#include +#include +#include + + +#if defined(_WIN32) && !defined(__WIN32__) +#define __WIN32__ +#endif + +#if defined(_MSC_VER) + +/* Avoid 'expression is always true' warning */ +#pragma warning(disable: 4296) + +#endif /* _MSC_VER */ + + +/* + * Alternative stdint.h and stdbool.h headers are supplied in include/c99 for + * systems that lack it. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +#if !defined(__HAIKU__) && !defined(__USE_MISC) +#if !defined(PIPE_OS_ANDROID) +typedef unsigned int uint; +#endif +typedef unsigned short ushort; +#endif +typedef unsigned char ubyte; + +typedef unsigned char boolean; +#ifndef TRUE +#define TRUE true +#endif +#ifndef FALSE +#define FALSE false +#endif + +#ifndef va_copy +#ifdef __va_copy +#define va_copy(dest, src) __va_copy((dest), (src)) +#else +#define va_copy(dest, src) (dest) = (src) +#endif +#endif + +/* Function visibility */ +#ifndef PUBLIC +# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) +# define PUBLIC __attribute__((visibility("default"))) +# elif defined(_MSC_VER) +# define PUBLIC __declspec(dllexport) +# else +# define PUBLIC +# endif +#endif + + +/* XXX: Use standard `__func__` instead */ +#ifndef __FUNCTION__ +# define __FUNCTION__ __func__ +#endif + + +/* This should match linux gcc cdecl semantics everywhere, so that we + * just codegen one calling convention on all platforms. + */ +#ifdef _MSC_VER +#define PIPE_CDECL __cdecl +#else +#define PIPE_CDECL +#endif + + + +#if defined(__GNUC__) +#define PIPE_DEPRECATED __attribute__((__deprecated__)) +#else +#define PIPE_DEPRECATED +#endif + + + +/* Macros for data alignment. */ +#if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) || defined(__SUNPRO_CC) + +/* See http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Type-Attributes.html */ +#define PIPE_ALIGN_TYPE(_alignment, _type) _type __attribute__((aligned(_alignment))) + +/* See http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Variable-Attributes.html */ +#define PIPE_ALIGN_VAR(_alignment) __attribute__((aligned(_alignment))) + +#if (__GNUC__ > 4 || (__GNUC__ == 4 &&__GNUC_MINOR__>1)) && !defined(PIPE_ARCH_X86_64) +#define PIPE_ALIGN_STACK __attribute__((force_align_arg_pointer)) +#else +#define PIPE_ALIGN_STACK +#endif + +#elif defined(_MSC_VER) + +/* See http://msdn.microsoft.com/en-us/library/83ythb65.aspx */ +#define PIPE_ALIGN_TYPE(_alignment, _type) __declspec(align(_alignment)) _type +#define PIPE_ALIGN_VAR(_alignment) __declspec(align(_alignment)) + +#define PIPE_ALIGN_STACK + +#elif defined(SWIG) + +#define PIPE_ALIGN_TYPE(_alignment, _type) _type +#define PIPE_ALIGN_VAR(_alignment) + +#define PIPE_ALIGN_STACK + +#else + +#error "Unsupported compiler" + +#endif + + +#if defined(__GNUC__) + +#define PIPE_READ_WRITE_BARRIER() __asm__("":::"memory") + +#elif defined(_MSC_VER) + +void _ReadWriteBarrier(void); +#pragma intrinsic(_ReadWriteBarrier) +#define PIPE_READ_WRITE_BARRIER() _ReadWriteBarrier() + +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) + +#define PIPE_READ_WRITE_BARRIER() __machine_rw_barrier() + +#else + +#warning "Unsupported compiler" +#define PIPE_READ_WRITE_BARRIER() /* */ + +#endif + + +/* You should use these macros to mark if blocks where the if condition + * is either likely to be true, or unlikely to be true. + * + * This will inform human readers of this fact, and will also inform + * the compiler, who will in turn inform the CPU. + * + * CPUs often start executing code inside the if or the else blocks + * without knowing whether the condition is true or not, and will have + * to throw the work away if they find out later they executed the + * wrong part of the if. + * + * If these macros are used, the CPU is more likely to correctly predict + * the right path, and will avoid speculatively executing the wrong branch, + * thus not throwing away work, resulting in better performance. + * + * In light of this, it is also a good idea to mark as "likely" a path + * which is not necessarily always more likely, but that will benefit much + * more from performance improvements since it is already much faster than + * the other path, or viceversa with "unlikely". + * + * Example usage: + * if(unlikely(do_we_need_a_software_fallback())) + * do_software_fallback(); + * else + * render_with_gpu(); + * + * The macros follow the Linux kernel convention, and more examples can + * be found there. + * + * Note that profile guided optimization can offer better results, but + * needs an appropriate coverage suite and does not inform human readers. + */ +#ifndef likely +# if defined(__GNUC__) +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +# else +# define likely(x) (x) +# define unlikely(x) (x) +# endif +#endif + + +/** + * Static (compile-time) assertion. + * Basically, use COND to dimension an array. If COND is false/zero the + * array size will be -1 and we'll get a compilation error. + */ +#define STATIC_ASSERT(COND) \ + do { \ + (void) sizeof(char [1 - 2*!(COND)]); \ + } while (0) + + +#if defined(__cplusplus) +} +#endif + + +#endif /* P_COMPILER_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_config.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_config.h new file mode 100644 index 0000000..d603681 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_config.h @@ -0,0 +1,259 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * Gallium configuration defines. + * + * This header file sets several defines based on the compiler, processor + * architecture, and operating system being used. These defines should be used + * throughout the code to facilitate porting to new platforms. It is likely that + * this file is auto-generated by an autoconf-like tool at some point, as some + * things cannot be determined by pre-defined environment alone. + * + * See also: + * - http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html + * - echo | gcc -dM -E - | sort + * - http://msdn.microsoft.com/en-us/library/b0084kay.aspx + * + * @author José Fonseca + */ + +#ifndef P_CONFIG_H_ +#define P_CONFIG_H_ + +#include +/* + * Compiler + */ + +#if defined(__GNUC__) +#define PIPE_CC_GCC +#define PIPE_CC_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +#endif + +/* + * Meaning of _MSC_VER value: + * - 1800: Visual Studio 2013 + * - 1700: Visual Studio 2012 + * - 1600: Visual Studio 2010 + * - 1500: Visual Studio 2008 + * - 1400: Visual C++ 2005 + * - 1310: Visual C++ .NET 2003 + * - 1300: Visual C++ .NET 2002 + * + * __MSC__ seems to be an old macro -- it is not pre-defined on recent MSVC + * versions. + */ +#if defined(_MSC_VER) || defined(__MSC__) +#define PIPE_CC_MSVC +#endif + +#if defined(__ICL) +#define PIPE_CC_ICL +#endif + +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#define PIPE_CC_SUNPRO +#endif + + +/* + * Processor architecture + */ + +#if defined(__i386__) /* gcc */ || defined(_M_IX86) /* msvc */ || defined(_X86_) || defined(__386__) || defined(i386) || defined(__i386) /* Sun cc */ +#define PIPE_ARCH_X86 +#endif + +#if defined(__x86_64__) /* gcc */ || defined(_M_X64) /* msvc */ || defined(_M_AMD64) /* msvc */ || defined(__x86_64) /* Sun cc */ +#define PIPE_ARCH_X86_64 +#endif + +#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) +#if defined(PIPE_CC_GCC) && !defined(__SSE2__) +/* #warning SSE2 support requires -msse -msse2 compiler options */ +#else +#define PIPE_ARCH_SSE +#endif +#if defined(PIPE_CC_GCC) && !defined(__SSSE3__) +/* #warning SSE3 support requires -msse3 compiler options */ +#else +#define PIPE_ARCH_SSSE3 +#endif +#endif + +#if defined(__ppc__) || defined(__ppc64__) || defined(__PPC__) +#define PIPE_ARCH_PPC +#if defined(__ppc64__) || defined(__PPC64__) +#define PIPE_ARCH_PPC_64 +#endif +#endif + +#if defined(__s390x__) +#define PIPE_ARCH_S390 +#endif + +#if defined(__arm__) +#define PIPE_ARCH_ARM +#endif + +#if defined(__aarch64__) +#define PIPE_ARCH_AARCH64 +#endif + +/* + * Endian detection. + */ + +#ifdef __GLIBC__ +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define PIPE_ARCH_LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# define PIPE_ARCH_BIG_ENDIAN +#endif + +#elif defined(__APPLE__) +#include + +#if __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN +# define PIPE_ARCH_LITTLE_ENDIAN +#elif __DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN +# define PIPE_ARCH_BIG_ENDIAN +#endif + +#elif defined(__sun) +#include + +#if defined(_LITTLE_ENDIAN) +# define PIPE_ARCH_LITTLE_ENDIAN +#elif defined(_BIG_ENDIAN) +# define PIPE_ARCH_BIG_ENDIAN +#endif + +#else + +#if defined(PIPE_ARCH_X86) || defined(PIPE_ARCH_X86_64) || defined(PIPE_ARCH_ARM) || defined(PIPE_ARCH_AARCH64) +#define PIPE_ARCH_LITTLE_ENDIAN +#elif defined(PIPE_ARCH_PPC) || defined(PIPE_ARCH_PPC_64) || defined(PIPE_ARCH_S390) +#define PIPE_ARCH_BIG_ENDIAN +#endif + +#endif + +#if !defined(PIPE_ARCH_LITTLE_ENDIAN) && !defined(PIPE_ARCH_BIG_ENDIAN) +#error Unknown Endianness +#endif + +/* + * Auto-detect the operating system family. + * + * See subsystem below for a more fine-grained distinction. + */ + +#if defined(__linux__) +#define PIPE_OS_LINUX +#define PIPE_OS_UNIX +#endif + +/* + * Android defines __linux__ so PIPE_OS_LINUX and PIPE_OS_UNIX will also be + * defined. + */ +#if defined(ANDROID) +#define PIPE_OS_ANDROID +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#define PIPE_OS_FREEBSD +#define PIPE_OS_BSD +#define PIPE_OS_UNIX +#endif + +#if defined(__OpenBSD__) +#define PIPE_OS_OPENBSD +#define PIPE_OS_BSD +#define PIPE_OS_UNIX +#endif + +#if defined(__NetBSD__) +#define PIPE_OS_NETBSD +#define PIPE_OS_BSD +#define PIPE_OS_UNIX +#endif + +#if defined(__GNU__) +#define PIPE_OS_HURD +#define PIPE_OS_UNIX +#endif + +#if defined(__sun) +#define PIPE_OS_SOLARIS +#define PIPE_OS_UNIX +#endif + +#if defined(__APPLE__) +#define PIPE_OS_APPLE +#define PIPE_OS_UNIX +#endif + +#if defined(_WIN32) || defined(WIN32) +#define PIPE_OS_WINDOWS +#endif + +#if defined(__HAIKU__) +#define PIPE_OS_HAIKU +#define PIPE_OS_UNIX +#endif + +#if defined(__CYGWIN__) +#define PIPE_OS_CYGWIN +#define PIPE_OS_UNIX +#endif + +/* + * Try to auto-detect the subsystem. + * + * NOTE: There is no way to auto-detect most of these. + */ + +#if defined(PIPE_OS_LINUX) || defined(PIPE_OS_BSD) || defined(PIPE_OS_SOLARIS) +#define PIPE_SUBSYSTEM_DRI +#endif /* PIPE_OS_LINUX || PIPE_OS_BSD || PIPE_OS_SOLARIS */ + +#if defined(PIPE_OS_WINDOWS) +#if defined(PIPE_SUBSYSTEM_WINDOWS_USER) +/* Windows User-space Library */ +#else +#define PIPE_SUBSYSTEM_WINDOWS_USER +#endif +#endif /* PIPE_OS_WINDOWS */ + + +#endif /* P_CONFIG_H_ */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_context.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_context.h new file mode 100644 index 0000000..0702729 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_context.h @@ -0,0 +1,546 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef PIPE_CONTEXT_H +#define PIPE_CONTEXT_H + +#include "p_compiler.h" +#include "p_format.h" +#include "p_video_enums.h" +#include "p_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +struct pipe_blend_color; +struct pipe_blend_state; +struct pipe_blit_info; +struct pipe_box; +struct pipe_clip_state; +struct pipe_constant_buffer; +struct pipe_depth_stencil_alpha_state; +struct pipe_draw_info; +struct pipe_fence_handle; +struct pipe_framebuffer_state; +struct pipe_index_buffer; +struct pipe_query; +struct pipe_poly_stipple; +struct pipe_rasterizer_state; +struct pipe_resolve_info; +struct pipe_resource; +struct pipe_sampler_state; +struct pipe_sampler_view; +struct pipe_scissor_state; +struct pipe_shader_state; +struct pipe_stencil_ref; +struct pipe_stream_output_target; +struct pipe_surface; +struct pipe_transfer; +struct pipe_vertex_buffer; +struct pipe_vertex_element; +struct pipe_video_buffer; +struct pipe_video_codec; +struct pipe_viewport_state; +struct pipe_compute_state; +union pipe_color_union; +union pipe_query_result; + +/** + * Gallium rendering context. Basically: + * - state setting functions + * - VBO drawing functions + * - surface functions + */ +struct pipe_context { + struct pipe_screen *screen; + + void *priv; /**< context private data (for DRI for example) */ + void *draw; /**< private, for draw module (temporary?) */ + + void (*destroy)( struct pipe_context * ); + + /** + * VBO drawing + */ + /*@{*/ + void (*draw_vbo)( struct pipe_context *pipe, + const struct pipe_draw_info *info ); + /*@}*/ + + /** + * Predicate subsequent rendering on occlusion query result + * \param query the query predicate, or NULL if no predicate + * \param condition whether to skip on FALSE or TRUE query results + * \param mode one of PIPE_RENDER_COND_x + */ + void (*render_condition)( struct pipe_context *pipe, + struct pipe_query *query, + boolean condition, + uint mode ); + + /** + * Query objects + */ + /*@{*/ + struct pipe_query *(*create_query)( struct pipe_context *pipe, + unsigned query_type ); + + void (*destroy_query)(struct pipe_context *pipe, + struct pipe_query *q); + + void (*begin_query)(struct pipe_context *pipe, struct pipe_query *q); + void (*end_query)(struct pipe_context *pipe, struct pipe_query *q); + + /** + * Get results of a query. + * \param wait if true, this query will block until the result is ready + * \return TRUE if results are ready, FALSE otherwise + */ + boolean (*get_query_result)(struct pipe_context *pipe, + struct pipe_query *q, + boolean wait, + union pipe_query_result *result); + /*@}*/ + + /** + * State functions (create/bind/destroy state objects) + */ + /*@{*/ + void * (*create_blend_state)(struct pipe_context *, + const struct pipe_blend_state *); + void (*bind_blend_state)(struct pipe_context *, void *); + void (*delete_blend_state)(struct pipe_context *, void *); + + void * (*create_sampler_state)(struct pipe_context *, + const struct pipe_sampler_state *); + void (*bind_sampler_states)(struct pipe_context *, + unsigned shader, unsigned start_slot, + unsigned num_samplers, void **samplers); + void (*delete_sampler_state)(struct pipe_context *, void *); + + void * (*create_rasterizer_state)(struct pipe_context *, + const struct pipe_rasterizer_state *); + void (*bind_rasterizer_state)(struct pipe_context *, void *); + void (*delete_rasterizer_state)(struct pipe_context *, void *); + + void * (*create_depth_stencil_alpha_state)(struct pipe_context *, + const struct pipe_depth_stencil_alpha_state *); + void (*bind_depth_stencil_alpha_state)(struct pipe_context *, void *); + void (*delete_depth_stencil_alpha_state)(struct pipe_context *, void *); + + void * (*create_fs_state)(struct pipe_context *, + const struct pipe_shader_state *); + void (*bind_fs_state)(struct pipe_context *, void *); + void (*delete_fs_state)(struct pipe_context *, void *); + + void * (*create_vs_state)(struct pipe_context *, + const struct pipe_shader_state *); + void (*bind_vs_state)(struct pipe_context *, void *); + void (*delete_vs_state)(struct pipe_context *, void *); + + void * (*create_gs_state)(struct pipe_context *, + const struct pipe_shader_state *); + void (*bind_gs_state)(struct pipe_context *, void *); + void (*delete_gs_state)(struct pipe_context *, void *); + + void * (*create_vertex_elements_state)(struct pipe_context *, + unsigned num_elements, + const struct pipe_vertex_element *); + void (*bind_vertex_elements_state)(struct pipe_context *, void *); + void (*delete_vertex_elements_state)(struct pipe_context *, void *); + + /*@}*/ + + /** + * Parameter-like state (or properties) + */ + /*@{*/ + void (*set_blend_color)( struct pipe_context *, + const struct pipe_blend_color * ); + + void (*set_stencil_ref)( struct pipe_context *, + const struct pipe_stencil_ref * ); + + void (*set_sample_mask)( struct pipe_context *, + unsigned sample_mask ); + + void (*set_clip_state)( struct pipe_context *, + const struct pipe_clip_state * ); + + void (*set_constant_buffer)( struct pipe_context *, + uint shader, uint index, + struct pipe_constant_buffer *buf ); + + void (*set_framebuffer_state)( struct pipe_context *, + const struct pipe_framebuffer_state * ); + + void (*set_polygon_stipple)( struct pipe_context *, + const struct pipe_poly_stipple * ); + + void (*set_scissor_states)( struct pipe_context *, + unsigned start_slot, + unsigned num_scissors, + const struct pipe_scissor_state * ); + + void (*set_viewport_states)( struct pipe_context *, + unsigned start_slot, + unsigned num_viewports, + const struct pipe_viewport_state *); + + void (*set_sampler_views)(struct pipe_context *, unsigned shader, + unsigned start_slot, unsigned num_views, + struct pipe_sampler_view **); + + /** + * Bind an array of shader resources that will be used by the + * graphics pipeline. Any resources that were previously bound to + * the specified range will be unbound after this call. + * + * \param start first resource to bind. + * \param count number of consecutive resources to bind. + * \param resources array of pointers to the resources to bind, it + * should contain at least \a count elements + * unless it's NULL, in which case no new + * resources will be bound. + */ + void (*set_shader_resources)(struct pipe_context *, + unsigned start, unsigned count, + struct pipe_surface **resources); + + void (*set_vertex_buffers)( struct pipe_context *, + unsigned start_slot, + unsigned num_buffers, + const struct pipe_vertex_buffer * ); + + void (*set_index_buffer)( struct pipe_context *pipe, + const struct pipe_index_buffer * ); + + /*@}*/ + + /** + * Stream output functions. + */ + /*@{*/ + + struct pipe_stream_output_target *(*create_stream_output_target)( + struct pipe_context *, + struct pipe_resource *, + unsigned buffer_offset, + unsigned buffer_size); + + void (*stream_output_target_destroy)(struct pipe_context *, + struct pipe_stream_output_target *); + + void (*set_stream_output_targets)(struct pipe_context *, + unsigned num_targets, + struct pipe_stream_output_target **targets, + unsigned append_bitmask); + + /*@}*/ + + + /** + * Resource functions for blit-like functionality + * + * If a driver supports multisampling, blit must implement color resolve. + */ + /*@{*/ + + /** + * Copy a block of pixels from one resource to another. + * The resource must be of the same format. + * Resources with nr_samples > 1 are not allowed. + */ + void (*resource_copy_region)(struct pipe_context *pipe, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box); + + /* Optimal hardware path for blitting pixels. + * Scaling, format conversion, up- and downsampling (resolve) are allowed. + */ + void (*blit)(struct pipe_context *pipe, + const struct pipe_blit_info *info); + + /*@}*/ + + /** + * Clear the specified set of currently bound buffers to specified values. + * The entire buffers are cleared (no scissor, no colormask, etc). + * + * \param buffers bitfield of PIPE_CLEAR_* values. + * \param color pointer to a union of fiu array for each of r, g, b, a. + * \param depth depth clear value in [0,1]. + * \param stencil stencil clear value + */ + void (*clear)(struct pipe_context *pipe, + unsigned buffers, + const union pipe_color_union *color, + double depth, + unsigned stencil); + + /** + * Clear a color rendertarget surface. + * \param color pointer to an union of fiu array for each of r, g, b, a. + */ + void (*clear_render_target)(struct pipe_context *pipe, + struct pipe_surface *dst, + const union pipe_color_union *color, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height); + + /** + * Clear a depth-stencil surface. + * \param clear_flags bitfield of PIPE_CLEAR_DEPTH/STENCIL values. + * \param depth depth clear value in [0,1]. + * \param stencil stencil clear value + */ + void (*clear_depth_stencil)(struct pipe_context *pipe, + struct pipe_surface *dst, + unsigned clear_flags, + double depth, + unsigned stencil, + unsigned dstx, unsigned dsty, + unsigned width, unsigned height); + + /** Flush draw commands + * + * \param flags bitfield of enum pipe_flush_flags values. + */ + void (*flush)(struct pipe_context *pipe, + struct pipe_fence_handle **fence, + unsigned flags); + + /** + * Create a view on a texture to be used by a shader stage. + */ + struct pipe_sampler_view * (*create_sampler_view)(struct pipe_context *ctx, + struct pipe_resource *texture, + const struct pipe_sampler_view *templat); + + void (*sampler_view_destroy)(struct pipe_context *ctx, + struct pipe_sampler_view *view); + + + /** + * Get a surface which is a "view" into a resource, used by + * render target / depth stencil stages. + */ + struct pipe_surface *(*create_surface)(struct pipe_context *ctx, + struct pipe_resource *resource, + const struct pipe_surface *templat); + + void (*surface_destroy)(struct pipe_context *ctx, + struct pipe_surface *); + + /** + * Map a resource. + * + * Transfers are (by default) context-private and allow uploads to be + * interleaved with rendering. + * + * out_transfer will contain the transfer object that must be passed + * to all the other transfer functions. It also contains useful + * information (like texture strides). + */ + void *(*transfer_map)(struct pipe_context *, + struct pipe_resource *resource, + unsigned level, + unsigned usage, /* a combination of PIPE_TRANSFER_x */ + const struct pipe_box *, + struct pipe_transfer **out_transfer); + + /* If transfer was created with WRITE|FLUSH_EXPLICIT, only the + * regions specified with this call are guaranteed to be written to + * the resource. + */ + void (*transfer_flush_region)( struct pipe_context *, + struct pipe_transfer *transfer, + const struct pipe_box *); + + void (*transfer_unmap)(struct pipe_context *, + struct pipe_transfer *transfer); + + /* One-shot transfer operation with data supplied in a user + * pointer. XXX: strides?? + */ + void (*transfer_inline_write)( struct pipe_context *, + struct pipe_resource *, + unsigned level, + unsigned usage, /* a combination of PIPE_TRANSFER_x */ + const struct pipe_box *, + const void *data, + unsigned stride, + unsigned layer_stride); + + /** + * Flush any pending framebuffer writes and invalidate texture caches. + */ + void (*texture_barrier)(struct pipe_context *); + + /** + * Flush caches according to flags. + */ + void (*memory_barrier)(struct pipe_context *, unsigned flags); + + /** + * Creates a video codec for a specific video format/profile + */ + struct pipe_video_codec *(*create_video_codec)( struct pipe_context *context, + const struct pipe_video_codec *templat ); + + /** + * Creates a video buffer as decoding target + */ + struct pipe_video_buffer *(*create_video_buffer)( struct pipe_context *context, + const struct pipe_video_buffer *templat ); + + /** + * Compute kernel execution + */ + /*@{*/ + /** + * Define the compute program and parameters to be used by + * pipe_context::launch_grid. + */ + void *(*create_compute_state)(struct pipe_context *context, + const struct pipe_compute_state *); + void (*bind_compute_state)(struct pipe_context *, void *); + void (*delete_compute_state)(struct pipe_context *, void *); + + /** + * Bind an array of shader resources that will be used by the + * compute program. Any resources that were previously bound to + * the specified range will be unbound after this call. + * + * \param start first resource to bind. + * \param count number of consecutive resources to bind. + * \param resources array of pointers to the resources to bind, it + * should contain at least \a count elements + * unless it's NULL, in which case no new + * resources will be bound. + */ + void (*set_compute_resources)(struct pipe_context *, + unsigned start, unsigned count, + struct pipe_surface **resources); + + /** + * Bind an array of buffers to be mapped into the address space of + * the GLOBAL resource. Any buffers that were previously bound + * between [first, first + count - 1] are unbound after this call. + * + * \param first first buffer to map. + * \param count number of consecutive buffers to map. + * \param resources array of pointers to the buffers to map, it + * should contain at least \a count elements + * unless it's NULL, in which case no new + * resources will be bound. + * \param handles array of pointers to the memory locations that + * will be updated with the address each buffer + * will be mapped to. The base memory address of + * each of the buffers will be added to the value + * pointed to by its corresponding handle to form + * the final address argument. It should contain + * at least \a count elements, unless \a + * resources is NULL in which case \a handles + * should be NULL as well. + * + * Note that the driver isn't required to make any guarantees about + * the contents of the \a handles array being valid anytime except + * during the subsequent calls to pipe_context::launch_grid. This + * means that the only sensible location handles[i] may point to is + * somewhere within the INPUT buffer itself. This is so to + * accommodate implementations that lack virtual memory but + * nevertheless migrate buffers on the fly, leading to resource + * base addresses that change on each kernel invocation or are + * unknown to the pipe driver. + */ + void (*set_global_binding)(struct pipe_context *context, + unsigned first, unsigned count, + struct pipe_resource **resources, + uint32_t **handles); + + /** + * Launch the compute kernel starting from instruction \a pc of the + * currently bound compute program. + * + * \a grid_layout and \a block_layout are arrays of size \a + * PIPE_COMPUTE_CAP_GRID_DIMENSION that determine the layout of the + * grid (in block units) and working block (in thread units) to be + * used, respectively. + * + * \a pc For drivers that use PIPE_SHADER_IR_LLVM as their prefered IR, + * this value will be the index of the kernel in the opencl.kernels + * metadata list. + * + * \a input will be used to initialize the INPUT resource, and it + * should point to a buffer of at least + * pipe_compute_state::req_input_mem bytes. + */ + void (*launch_grid)(struct pipe_context *context, + const uint *block_layout, const uint *grid_layout, + uint32_t pc, const void *input); + /*@}*/ + + /** + * Get sample position for an individual sample point. + * + * \param sample_count - total number of samples + * \param sample_index - sample to get the position values for + * \param out_value - return value of 2 floats for x and y position for + * requested sample. + */ + void (*get_sample_position)(struct pipe_context *context, + unsigned sample_count, + unsigned sample_index, + float *out_value); + + /** + * Flush the resource cache, so that the resource can be used + * by an external client. Possible usage: + * - flushing a resource before presenting it on the screen + * - flushing a resource if some other process or device wants to use it + * This shouldn't be used to flush caches if the resource is only managed + * by a single pipe_screen and is not shared with another process. + * (i.e. you shouldn't use it to flush caches explicitly if you want to e.g. + * use the resource for texturing) + */ + void (*flush_resource)(struct pipe_context *ctx, + struct pipe_resource *resource); +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* PIPE_CONTEXT_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_defines.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_defines.h new file mode 100644 index 0000000..610b92c --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_defines.h @@ -0,0 +1,806 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef PIPE_DEFINES_H +#define PIPE_DEFINES_H + +#include "p_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Gallium error codes. + * + * - A zero value always means success. + * - A negative value always means failure. + * - The meaning of a positive value is function dependent. + */ +enum pipe_error { + PIPE_OK = 0, + PIPE_ERROR = -1, /**< Generic error */ + PIPE_ERROR_BAD_INPUT = -2, + PIPE_ERROR_OUT_OF_MEMORY = -3, + PIPE_ERROR_RETRY = -4 + /* TODO */ +}; + + +#define PIPE_BLENDFACTOR_ONE 0x1 +#define PIPE_BLENDFACTOR_SRC_COLOR 0x2 +#define PIPE_BLENDFACTOR_SRC_ALPHA 0x3 +#define PIPE_BLENDFACTOR_DST_ALPHA 0x4 +#define PIPE_BLENDFACTOR_DST_COLOR 0x5 +#define PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE 0x6 +#define PIPE_BLENDFACTOR_CONST_COLOR 0x7 +#define PIPE_BLENDFACTOR_CONST_ALPHA 0x8 +#define PIPE_BLENDFACTOR_SRC1_COLOR 0x9 +#define PIPE_BLENDFACTOR_SRC1_ALPHA 0x0A +#define PIPE_BLENDFACTOR_ZERO 0x11 +#define PIPE_BLENDFACTOR_INV_SRC_COLOR 0x12 +#define PIPE_BLENDFACTOR_INV_SRC_ALPHA 0x13 +#define PIPE_BLENDFACTOR_INV_DST_ALPHA 0x14 +#define PIPE_BLENDFACTOR_INV_DST_COLOR 0x15 +#define PIPE_BLENDFACTOR_INV_CONST_COLOR 0x17 +#define PIPE_BLENDFACTOR_INV_CONST_ALPHA 0x18 +#define PIPE_BLENDFACTOR_INV_SRC1_COLOR 0x19 +#define PIPE_BLENDFACTOR_INV_SRC1_ALPHA 0x1A + +#define PIPE_BLEND_ADD 0 +#define PIPE_BLEND_SUBTRACT 1 +#define PIPE_BLEND_REVERSE_SUBTRACT 2 +#define PIPE_BLEND_MIN 3 +#define PIPE_BLEND_MAX 4 + + +enum pipe_logicop { + PIPE_LOGICOP_CLEAR, + PIPE_LOGICOP_NOR, + PIPE_LOGICOP_AND_INVERTED, + PIPE_LOGICOP_COPY_INVERTED, + PIPE_LOGICOP_AND_REVERSE, + PIPE_LOGICOP_INVERT, + PIPE_LOGICOP_XOR, + PIPE_LOGICOP_NAND, + PIPE_LOGICOP_AND, + PIPE_LOGICOP_EQUIV, + PIPE_LOGICOP_NOOP, + PIPE_LOGICOP_OR_INVERTED, + PIPE_LOGICOP_COPY, + PIPE_LOGICOP_OR_REVERSE, + PIPE_LOGICOP_OR, + PIPE_LOGICOP_SET, +}; + +#define PIPE_MASK_R 0x1 +#define PIPE_MASK_G 0x2 +#define PIPE_MASK_B 0x4 +#define PIPE_MASK_A 0x8 +#define PIPE_MASK_RGBA 0xf +#define PIPE_MASK_Z 0x10 +#define PIPE_MASK_S 0x20 +#define PIPE_MASK_ZS 0x30 +#define PIPE_MASK_RGBAZS (PIPE_MASK_RGBA|PIPE_MASK_ZS) + + +/** + * Inequality functions. Used for depth test, stencil compare, alpha + * test, shadow compare, etc. + */ +#define PIPE_FUNC_NEVER 0 +#define PIPE_FUNC_LESS 1 +#define PIPE_FUNC_EQUAL 2 +#define PIPE_FUNC_LEQUAL 3 +#define PIPE_FUNC_GREATER 4 +#define PIPE_FUNC_NOTEQUAL 5 +#define PIPE_FUNC_GEQUAL 6 +#define PIPE_FUNC_ALWAYS 7 + +/** Polygon fill mode */ +#define PIPE_POLYGON_MODE_FILL 0 +#define PIPE_POLYGON_MODE_LINE 1 +#define PIPE_POLYGON_MODE_POINT 2 + +/** Polygon face specification, eg for culling */ +#define PIPE_FACE_NONE 0 +#define PIPE_FACE_FRONT 1 +#define PIPE_FACE_BACK 2 +#define PIPE_FACE_FRONT_AND_BACK (PIPE_FACE_FRONT | PIPE_FACE_BACK) + +/** Stencil ops */ +#define PIPE_STENCIL_OP_KEEP 0 +#define PIPE_STENCIL_OP_ZERO 1 +#define PIPE_STENCIL_OP_REPLACE 2 +#define PIPE_STENCIL_OP_INCR 3 +#define PIPE_STENCIL_OP_DECR 4 +#define PIPE_STENCIL_OP_INCR_WRAP 5 +#define PIPE_STENCIL_OP_DECR_WRAP 6 +#define PIPE_STENCIL_OP_INVERT 7 + +/** Texture types. + * See the documentation for info on PIPE_TEXTURE_RECT vs PIPE_TEXTURE_2D */ +enum pipe_texture_target { + PIPE_BUFFER = 0, + PIPE_TEXTURE_1D = 1, + PIPE_TEXTURE_2D = 2, + PIPE_TEXTURE_3D = 3, + PIPE_TEXTURE_CUBE = 4, + PIPE_TEXTURE_RECT = 5, + PIPE_TEXTURE_1D_ARRAY = 6, + PIPE_TEXTURE_2D_ARRAY = 7, + PIPE_TEXTURE_CUBE_ARRAY = 8, + PIPE_MAX_TEXTURE_TYPES +}; + +#define PIPE_TEX_FACE_POS_X 0 +#define PIPE_TEX_FACE_NEG_X 1 +#define PIPE_TEX_FACE_POS_Y 2 +#define PIPE_TEX_FACE_NEG_Y 3 +#define PIPE_TEX_FACE_POS_Z 4 +#define PIPE_TEX_FACE_NEG_Z 5 +#define PIPE_TEX_FACE_MAX 6 + +#define PIPE_TEX_WRAP_REPEAT 0 +#define PIPE_TEX_WRAP_CLAMP 1 +#define PIPE_TEX_WRAP_CLAMP_TO_EDGE 2 +#define PIPE_TEX_WRAP_CLAMP_TO_BORDER 3 +#define PIPE_TEX_WRAP_MIRROR_REPEAT 4 +#define PIPE_TEX_WRAP_MIRROR_CLAMP 5 +#define PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE 6 +#define PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER 7 + +/* Between mipmaps, ie mipfilter + */ +#define PIPE_TEX_MIPFILTER_NEAREST 0 +#define PIPE_TEX_MIPFILTER_LINEAR 1 +#define PIPE_TEX_MIPFILTER_NONE 2 + +/* Within a mipmap, ie min/mag filter + */ +#define PIPE_TEX_FILTER_NEAREST 0 +#define PIPE_TEX_FILTER_LINEAR 1 + +#define PIPE_TEX_COMPARE_NONE 0 +#define PIPE_TEX_COMPARE_R_TO_TEXTURE 1 + +/** + * Clear buffer bits + */ +#define PIPE_CLEAR_DEPTH (1 << 0) +#define PIPE_CLEAR_STENCIL (1 << 1) +#define PIPE_CLEAR_COLOR0 (1 << 2) +#define PIPE_CLEAR_COLOR1 (1 << 3) +#define PIPE_CLEAR_COLOR2 (1 << 4) +#define PIPE_CLEAR_COLOR3 (1 << 5) +#define PIPE_CLEAR_COLOR4 (1 << 6) +#define PIPE_CLEAR_COLOR5 (1 << 7) +#define PIPE_CLEAR_COLOR6 (1 << 8) +#define PIPE_CLEAR_COLOR7 (1 << 9) +/** Combined flags */ +/** All color buffers currently bound */ +#define PIPE_CLEAR_COLOR (PIPE_CLEAR_COLOR0 | PIPE_CLEAR_COLOR1 | \ + PIPE_CLEAR_COLOR2 | PIPE_CLEAR_COLOR3 | \ + PIPE_CLEAR_COLOR4 | PIPE_CLEAR_COLOR5 | \ + PIPE_CLEAR_COLOR6 | PIPE_CLEAR_COLOR7) +#define PIPE_CLEAR_DEPTHSTENCIL (PIPE_CLEAR_DEPTH | PIPE_CLEAR_STENCIL) + +/** + * Transfer object usage flags + */ +enum pipe_transfer_usage { + /** + * Resource contents read back (or accessed directly) at transfer + * create time. + */ + PIPE_TRANSFER_READ = (1 << 0), + + /** + * Resource contents will be written back at transfer_unmap + * time (or modified as a result of being accessed directly). + */ + PIPE_TRANSFER_WRITE = (1 << 1), + + /** + * Read/modify/write + */ + PIPE_TRANSFER_READ_WRITE = PIPE_TRANSFER_READ | PIPE_TRANSFER_WRITE, + + /** + * The transfer should map the texture storage directly. The driver may + * return NULL if that isn't possible, and the state tracker needs to cope + * with that and use an alternative path without this flag. + * + * E.g. the state tracker could have a simpler path which maps textures and + * does read/modify/write cycles on them directly, and a more complicated + * path which uses minimal read and write transfers. + */ + PIPE_TRANSFER_MAP_DIRECTLY = (1 << 2), + + /** + * Discards the memory within the mapped region. + * + * It should not be used with PIPE_TRANSFER_READ. + * + * See also: + * - OpenGL's ARB_map_buffer_range extension, MAP_INVALIDATE_RANGE_BIT flag. + */ + PIPE_TRANSFER_DISCARD_RANGE = (1 << 8), + + /** + * Fail if the resource cannot be mapped immediately. + * + * See also: + * - Direct3D's D3DLOCK_DONOTWAIT flag. + * - Mesa3D's MESA_MAP_NOWAIT_BIT flag. + * - WDDM's D3DDDICB_LOCKFLAGS.DonotWait flag. + */ + PIPE_TRANSFER_DONTBLOCK = (1 << 9), + + /** + * Do not attempt to synchronize pending operations on the resource when mapping. + * + * It should not be used with PIPE_TRANSFER_READ. + * + * See also: + * - OpenGL's ARB_map_buffer_range extension, MAP_UNSYNCHRONIZED_BIT flag. + * - Direct3D's D3DLOCK_NOOVERWRITE flag. + * - WDDM's D3DDDICB_LOCKFLAGS.IgnoreSync flag. + */ + PIPE_TRANSFER_UNSYNCHRONIZED = (1 << 10), + + /** + * Written ranges will be notified later with + * pipe_context::transfer_flush_region. + * + * It should not be used with PIPE_TRANSFER_READ. + * + * See also: + * - pipe_context::transfer_flush_region + * - OpenGL's ARB_map_buffer_range extension, MAP_FLUSH_EXPLICIT_BIT flag. + */ + PIPE_TRANSFER_FLUSH_EXPLICIT = (1 << 11), + + /** + * Discards all memory backing the resource. + * + * It should not be used with PIPE_TRANSFER_READ. + * + * This is equivalent to: + * - OpenGL's ARB_map_buffer_range extension, MAP_INVALIDATE_BUFFER_BIT + * - BufferData(NULL) on a GL buffer + * - Direct3D's D3DLOCK_DISCARD flag. + * - WDDM's D3DDDICB_LOCKFLAGS.Discard flag. + * - D3D10 DDI's D3D10_DDI_MAP_WRITE_DISCARD flag + * - D3D10's D3D10_MAP_WRITE_DISCARD flag. + */ + PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE = (1 << 12), + + /** + * Allows the resource to be used for rendering while mapped. + * + * PIPE_RESOURCE_FLAG_MAP_PERSISTENT must be set when creating + * the resource. + * + * If COHERENT is not set, memory_barrier(PIPE_BARRIER_MAPPED_BUFFER) + * must be called to ensure the device can see what the CPU has written. + */ + PIPE_TRANSFER_PERSISTENT = (1 << 13), + + /** + * If PERSISTENT is set, this ensures any writes done by the device are + * immediately visible to the CPU and vice versa. + * + * PIPE_RESOURCE_FLAG_MAP_COHERENT must be set when creating + * the resource. + */ + PIPE_TRANSFER_COHERENT = (1 << 14) +}; + +/** + * Flags for the flush function. + */ +enum pipe_flush_flags { + PIPE_FLUSH_END_OF_FRAME = (1 << 0) +}; + +/** + * Flags for pipe_context::memory_barrier. + */ +#define PIPE_BARRIER_MAPPED_BUFFER (1 << 0) +#define PIPE_BARRIER_SHADER_BUFFER (1 << 1) +#define PIPE_BARRIER_QUERY_BUFFER (1 << 2) +#define PIPE_BARRIER_VERTEX_BUFFER (1 << 3) +#define PIPE_BARRIER_INDEX_BUFFER (1 << 4) +#define PIPE_BARRIER_CONSTANT_BUFFER (1 << 5) +#define PIPE_BARRIER_INDIRECT_BUFFER (1 << 6) +#define PIPE_BARRIER_TEXTURE (1 << 7) +#define PIPE_BARRIER_IMAGE (1 << 8) +#define PIPE_BARRIER_FRAMEBUFFER (1 << 9) +#define PIPE_BARRIER_STREAMOUT_BUFFER (1 << 10) +#define PIPE_BARRIER_GLOBAL_BUFFER (1 << 11) +#define PIPE_BARRIER_ALL ((1 << 12) - 1) + +/** + * Flags for pipe_context::texture_barrier. + */ +#define PIPE_TEXTURE_BARRIER_SAMPLER (1 << 0) +#define PIPE_TEXTURE_BARRIER_FRAMEBUFFER (1 << 1) + +/* + * Resource binding flags -- state tracker must specify in advance all + * the ways a resource might be used. + */ +#define PIPE_BIND_DEPTH_STENCIL (1 << 0) /* create_surface */ +#define PIPE_BIND_RENDER_TARGET (1 << 1) /* create_surface */ +#define PIPE_BIND_BLENDABLE (1 << 2) /* create_surface */ +#define PIPE_BIND_SAMPLER_VIEW (1 << 3) /* create_sampler_view */ +#define PIPE_BIND_VERTEX_BUFFER (1 << 4) /* set_vertex_buffers */ +#define PIPE_BIND_INDEX_BUFFER (1 << 5) /* draw_elements */ +#define PIPE_BIND_CONSTANT_BUFFER (1 << 6) /* set_constant_buffer */ +#define PIPE_BIND_DISPLAY_TARGET (1 << 8) /* flush_front_buffer */ +#define PIPE_BIND_TRANSFER_WRITE (1 << 9) /* transfer_map */ +#define PIPE_BIND_TRANSFER_READ (1 << 10) /* transfer_map */ +#define PIPE_BIND_STREAM_OUTPUT (1 << 11) /* set_stream_output_buffers */ +#define PIPE_BIND_CURSOR (1 << 16) /* mouse cursor */ +#define PIPE_BIND_CUSTOM (1 << 17) /* state-tracker/winsys usages */ +#define PIPE_BIND_GLOBAL (1 << 18) /* set_global_binding */ +#define PIPE_BIND_SHADER_RESOURCE (1 << 19) /* set_shader_resources */ +#define PIPE_BIND_COMPUTE_RESOURCE (1 << 20) /* set_compute_resources */ +#define PIPE_BIND_COMMAND_ARGS_BUFFER (1 << 21) /* pipe_draw_info.indirect */ +#define PIPE_BIND_QUERY_BUFFER (1 << 22) /* get_query_result_resource */ + +/* The first two flags above were previously part of the amorphous + * TEXTURE_USAGE, most of which are now descriptions of the ways a + * particular texture can be bound to the gallium pipeline. The two flags + * below do not fit within that and probably need to be migrated to some + * other place. + * + * It seems like scanout is used by the Xorg state tracker to ask for + * a texture suitable for actual scanout (hence the name), which + * implies extra layout constraints on some hardware. It may also + * have some special meaning regarding mouse cursor images. + * + * The shared flag is quite underspecified, but certainly isn't a + * binding flag - it seems more like a message to the winsys to create + * a shareable allocation. + * + * The third flag has been added to be able to force textures to be created + * in linear mode (no tiling). + */ +#define PIPE_BIND_SCANOUT (1 << 14) /* */ +#define PIPE_BIND_SHARED (1 << 15) /* get_texture_handle ??? */ +#define PIPE_BIND_LINEAR (1 << 21) + + +/* Flags for the driver about resource behaviour: + */ +#define PIPE_RESOURCE_FLAG_MAP_PERSISTENT (1 << 0) +#define PIPE_RESOURCE_FLAG_MAP_COHERENT (1 << 1) +#define PIPE_RESOURCE_FLAG_DRV_PRIV (1 << 16) /* driver/winsys private */ +#define PIPE_RESOURCE_FLAG_ST_PRIV (1 << 24) /* state-tracker/winsys private */ + +/* Hint about the expected lifecycle of a resource. + * Sorted according to GPU vs CPU access. + */ +#define PIPE_USAGE_DEFAULT 0 /* fast GPU access */ +#define PIPE_USAGE_IMMUTABLE 1 /* fast GPU access, immutable */ +#define PIPE_USAGE_DYNAMIC 2 /* uploaded data is used multiple times */ +#define PIPE_USAGE_STREAM 3 /* uploaded data is used once */ +#define PIPE_USAGE_STAGING 4 /* fast CPU access */ + + +/** + * Shaders + */ +#define PIPE_SHADER_VERTEX 0 +#define PIPE_SHADER_FRAGMENT 1 +#define PIPE_SHADER_GEOMETRY 2 +#define PIPE_SHADER_TESS_CTRL 3 +#define PIPE_SHADER_TESS_EVAL 4 +#define PIPE_SHADER_COMPUTE 5 +#define PIPE_SHADER_TYPES 6 + + +/** + * Primitive types: + */ +#define PIPE_PRIM_POINTS 0 +#define PIPE_PRIM_LINES 1 +#define PIPE_PRIM_LINE_LOOP 2 +#define PIPE_PRIM_LINE_STRIP 3 +#define PIPE_PRIM_TRIANGLES 4 +#define PIPE_PRIM_TRIANGLE_STRIP 5 +#define PIPE_PRIM_TRIANGLE_FAN 6 +#define PIPE_PRIM_QUADS 7 +#define PIPE_PRIM_QUAD_STRIP 8 +#define PIPE_PRIM_POLYGON 9 +#define PIPE_PRIM_LINES_ADJACENCY 10 +#define PIPE_PRIM_LINE_STRIP_ADJACENCY 11 +#define PIPE_PRIM_TRIANGLES_ADJACENCY 12 +#define PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY 13 +#define PIPE_PRIM_PATCHES 14 +#define PIPE_PRIM_MAX 15 + + +/** + * Tessellator spacing types + */ +#define PIPE_TESS_SPACING_FRACTIONAL_ODD 0 +#define PIPE_TESS_SPACING_FRACTIONAL_EVEN 1 +#define PIPE_TESS_SPACING_EQUAL 2 + +/** + * Query object types + */ +#define PIPE_QUERY_OCCLUSION_COUNTER 0 +#define PIPE_QUERY_OCCLUSION_PREDICATE 1 +#define PIPE_QUERY_TIMESTAMP 2 +#define PIPE_QUERY_TIMESTAMP_DISJOINT 3 +#define PIPE_QUERY_TIME_ELAPSED 4 +#define PIPE_QUERY_PRIMITIVES_GENERATED 5 +#define PIPE_QUERY_PRIMITIVES_EMITTED 6 +#define PIPE_QUERY_SO_STATISTICS 7 +#define PIPE_QUERY_SO_OVERFLOW_PREDICATE 8 +#define PIPE_QUERY_GPU_FINISHED 9 +#define PIPE_QUERY_PIPELINE_STATISTICS 10 +#define PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE 11 +#define PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE 12 +#define PIPE_QUERY_TYPES 13 + +/* start of driver queries, + * see pipe_screen::get_driver_query_info */ +#define PIPE_QUERY_DRIVER_SPECIFIC 256 + + +/** + * Conditional rendering modes + */ +#define PIPE_RENDER_COND_WAIT 0 +#define PIPE_RENDER_COND_NO_WAIT 1 +#define PIPE_RENDER_COND_BY_REGION_WAIT 2 +#define PIPE_RENDER_COND_BY_REGION_NO_WAIT 3 + + +/** + * Point sprite coord modes + */ +#define PIPE_SPRITE_COORD_UPPER_LEFT 0 +#define PIPE_SPRITE_COORD_LOWER_LEFT 1 + + +/** + * Texture swizzles + */ +#define PIPE_SWIZZLE_RED 0 +#define PIPE_SWIZZLE_GREEN 1 +#define PIPE_SWIZZLE_BLUE 2 +#define PIPE_SWIZZLE_ALPHA 3 +#define PIPE_SWIZZLE_ZERO 4 +#define PIPE_SWIZZLE_ONE 5 + + +#define PIPE_TIMEOUT_INFINITE 0xffffffffffffffffull + +/** + * pipe_image_view access flags. + */ +#define PIPE_IMAGE_ACCESS_READ (1 << 0) +#define PIPE_IMAGE_ACCESS_WRITE (1 << 1) +#define PIPE_IMAGE_ACCESS_READ_WRITE (PIPE_IMAGE_ACCESS_READ | \ + PIPE_IMAGE_ACCESS_WRITE) + +/** + * Implementation capabilities/limits which are queried through + * pipe_screen::get_param() + */ +enum pipe_cap { + PIPE_CAP_NPOT_TEXTURES = 1, + PIPE_CAP_TWO_SIDED_STENCIL = 2, + PIPE_CAP_MAX_DUAL_SOURCE_RENDER_TARGETS = 4, + PIPE_CAP_ANISOTROPIC_FILTER = 5, + PIPE_CAP_POINT_SPRITE = 6, + PIPE_CAP_MAX_RENDER_TARGETS = 7, + PIPE_CAP_OCCLUSION_QUERY = 8, + PIPE_CAP_QUERY_TIME_ELAPSED = 9, + PIPE_CAP_TEXTURE_SHADOW_MAP = 10, + PIPE_CAP_TEXTURE_SWIZZLE = 11, + PIPE_CAP_MAX_TEXTURE_2D_LEVELS = 12, + PIPE_CAP_MAX_TEXTURE_3D_LEVELS = 13, + PIPE_CAP_MAX_TEXTURE_CUBE_LEVELS = 14, + PIPE_CAP_TEXTURE_MIRROR_CLAMP = 25, + PIPE_CAP_BLEND_EQUATION_SEPARATE = 28, + PIPE_CAP_SM3 = 29, /*< Shader Model, supported */ + PIPE_CAP_MAX_STREAM_OUTPUT_BUFFERS = 30, + PIPE_CAP_PRIMITIVE_RESTART = 31, + /** blend enables and write masks per rendertarget */ + PIPE_CAP_INDEP_BLEND_ENABLE = 33, + /** different blend funcs per rendertarget */ + PIPE_CAP_INDEP_BLEND_FUNC = 34, + PIPE_CAP_MAX_TEXTURE_ARRAY_LAYERS = 36, + PIPE_CAP_TGSI_FS_COORD_ORIGIN_UPPER_LEFT = 37, + PIPE_CAP_TGSI_FS_COORD_ORIGIN_LOWER_LEFT = 38, + PIPE_CAP_TGSI_FS_COORD_PIXEL_CENTER_HALF_INTEGER = 39, + PIPE_CAP_TGSI_FS_COORD_PIXEL_CENTER_INTEGER = 40, + PIPE_CAP_DEPTH_CLIP_DISABLE = 41, + PIPE_CAP_SHADER_STENCIL_EXPORT = 42, + PIPE_CAP_TGSI_INSTANCEID = 43, + PIPE_CAP_VERTEX_ELEMENT_INSTANCE_DIVISOR = 44, + PIPE_CAP_FRAGMENT_COLOR_CLAMPED = 45, + PIPE_CAP_MIXED_COLORBUFFER_FORMATS = 46, + PIPE_CAP_SEAMLESS_CUBE_MAP = 47, + PIPE_CAP_SEAMLESS_CUBE_MAP_PER_TEXTURE = 48, + PIPE_CAP_MIN_TEXEL_OFFSET = 50, + PIPE_CAP_MAX_TEXEL_OFFSET = 51, + PIPE_CAP_CONDITIONAL_RENDER = 52, + PIPE_CAP_TEXTURE_BARRIER = 53, + PIPE_CAP_MAX_STREAM_OUTPUT_SEPARATE_COMPONENTS = 55, + PIPE_CAP_MAX_STREAM_OUTPUT_INTERLEAVED_COMPONENTS = 56, + PIPE_CAP_STREAM_OUTPUT_PAUSE_RESUME = 57, + PIPE_CAP_TGSI_CAN_COMPACT_CONSTANTS = 59, /* temporary */ + PIPE_CAP_VERTEX_COLOR_UNCLAMPED = 60, + PIPE_CAP_VERTEX_COLOR_CLAMPED = 61, + PIPE_CAP_GLSL_FEATURE_LEVEL = 62, + PIPE_CAP_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION = 63, + PIPE_CAP_USER_VERTEX_BUFFERS = 64, + PIPE_CAP_VERTEX_BUFFER_OFFSET_4BYTE_ALIGNED_ONLY = 65, + PIPE_CAP_VERTEX_BUFFER_STRIDE_4BYTE_ALIGNED_ONLY = 66, + PIPE_CAP_VERTEX_ELEMENT_SRC_OFFSET_4BYTE_ALIGNED_ONLY = 67, + PIPE_CAP_COMPUTE = 68, + PIPE_CAP_USER_INDEX_BUFFERS = 69, + PIPE_CAP_USER_CONSTANT_BUFFERS = 70, + PIPE_CAP_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 71, + PIPE_CAP_START_INSTANCE = 72, + PIPE_CAP_QUERY_TIMESTAMP = 73, + PIPE_CAP_TEXTURE_MULTISAMPLE = 74, + PIPE_CAP_MIN_MAP_BUFFER_ALIGNMENT = 75, + PIPE_CAP_CUBE_MAP_ARRAY = 76, + PIPE_CAP_TEXTURE_BUFFER_OBJECTS = 77, + PIPE_CAP_TEXTURE_BUFFER_OFFSET_ALIGNMENT = 78, + PIPE_CAP_TGSI_TEXCOORD = 79, + PIPE_CAP_PREFER_BLIT_BASED_TEXTURE_TRANSFER = 80, + PIPE_CAP_QUERY_PIPELINE_STATISTICS = 81, + PIPE_CAP_TEXTURE_BORDER_COLOR_QUIRK = 82, + PIPE_CAP_MAX_TEXTURE_BUFFER_SIZE = 83, + PIPE_CAP_MAX_VIEWPORTS = 84, + PIPE_CAP_ENDIANNESS = 85, + PIPE_CAP_MIXED_FRAMEBUFFER_SIZES = 86, + PIPE_CAP_TGSI_VS_LAYER_VIEWPORT = 87, + PIPE_CAP_MAX_GEOMETRY_OUTPUT_VERTICES = 88, + PIPE_CAP_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = 89, + PIPE_CAP_MAX_TEXTURE_GATHER_COMPONENTS = 90, + PIPE_CAP_TEXTURE_GATHER_SM5 = 91, + PIPE_CAP_BUFFER_MAP_PERSISTENT_COHERENT = 92, + PIPE_CAP_FAKE_SW_MSAA = 93, + PIPE_CAP_TEXTURE_QUERY_LOD = 94, + PIPE_CAP_MIN_TEXTURE_GATHER_OFFSET = 95, + PIPE_CAP_MAX_TEXTURE_GATHER_OFFSET = 96, + PIPE_CAP_SAMPLE_SHADING = 97, + PIPE_CAP_TEXTURE_GATHER_OFFSETS = 98, + PIPE_CAP_TGSI_VS_WINDOW_SPACE_POSITION = 99, + PIPE_CAP_MAX_VERTEX_STREAMS = 100, + PIPE_CAP_DRAW_INDIRECT = 101, + PIPE_CAP_TGSI_FS_FINE_DERIVATIVE = 102, + PIPE_CAP_VENDOR_ID = 103, + PIPE_CAP_DEVICE_ID = 104, + PIPE_CAP_ACCELERATED = 105, + PIPE_CAP_VIDEO_MEMORY = 106, + PIPE_CAP_UMA = 107, + PIPE_CAP_CONDITIONAL_RENDER_INVERTED = 108, + PIPE_CAP_MAX_VERTEX_ATTRIB_STRIDE = 109, + PIPE_CAP_SAMPLER_VIEW_TARGET = 110, + PIPE_CAP_CLIP_HALFZ = 111, + PIPE_CAP_VERTEXID_NOBASE = 112, + PIPE_CAP_POLYGON_OFFSET_CLAMP = 113, +}; + +#define PIPE_QUIRK_TEXTURE_BORDER_COLOR_SWIZZLE_NV50 (1 << 0) +#define PIPE_QUIRK_TEXTURE_BORDER_COLOR_SWIZZLE_R600 (1 << 1) + +enum pipe_endian { + PIPE_ENDIAN_LITTLE = 0, + PIPE_ENDIAN_BIG = 1, +#if defined(PIPE_ARCH_LITTLE_ENDIAN) + PIPE_ENDIAN_NATIVE = PIPE_ENDIAN_LITTLE +#elif defined(PIPE_ARCH_BIG_ENDIAN) + PIPE_ENDIAN_NATIVE = PIPE_ENDIAN_BIG +#endif +}; + +/** + * Implementation limits which are queried through + * pipe_screen::get_paramf() + */ +enum pipe_capf +{ + PIPE_CAPF_MAX_LINE_WIDTH, + PIPE_CAPF_MAX_LINE_WIDTH_AA, + PIPE_CAPF_MAX_POINT_WIDTH, + PIPE_CAPF_MAX_POINT_WIDTH_AA, + PIPE_CAPF_MAX_TEXTURE_ANISOTROPY, + PIPE_CAPF_MAX_TEXTURE_LOD_BIAS, + PIPE_CAPF_GUARD_BAND_LEFT, + PIPE_CAPF_GUARD_BAND_TOP, + PIPE_CAPF_GUARD_BAND_RIGHT, + PIPE_CAPF_GUARD_BAND_BOTTOM +}; + +/* Shader caps not specific to any single stage */ +enum pipe_shader_cap +{ + PIPE_SHADER_CAP_MAX_INSTRUCTIONS, /* if 0, it means the stage is unsupported */ + PIPE_SHADER_CAP_MAX_ALU_INSTRUCTIONS, + PIPE_SHADER_CAP_MAX_TEX_INSTRUCTIONS, + PIPE_SHADER_CAP_MAX_TEX_INDIRECTIONS, + PIPE_SHADER_CAP_MAX_CONTROL_FLOW_DEPTH, + PIPE_SHADER_CAP_MAX_INPUTS, + PIPE_SHADER_CAP_MAX_OUTPUTS, + PIPE_SHADER_CAP_MAX_CONST_BUFFER_SIZE, + PIPE_SHADER_CAP_MAX_CONST_BUFFERS, + PIPE_SHADER_CAP_MAX_TEMPS, + PIPE_SHADER_CAP_MAX_PREDS, + /* boolean caps */ + PIPE_SHADER_CAP_TGSI_CONT_SUPPORTED, + PIPE_SHADER_CAP_INDIRECT_INPUT_ADDR, + PIPE_SHADER_CAP_INDIRECT_OUTPUT_ADDR, + PIPE_SHADER_CAP_INDIRECT_TEMP_ADDR, + PIPE_SHADER_CAP_INDIRECT_CONST_ADDR, + PIPE_SHADER_CAP_SUBROUTINES, /* BGNSUB, ENDSUB, CAL, RET */ + PIPE_SHADER_CAP_INTEGERS, + PIPE_SHADER_CAP_MAX_TEXTURE_SAMPLERS, + PIPE_SHADER_CAP_PREFERRED_IR, + PIPE_SHADER_CAP_TGSI_SQRT_SUPPORTED, + PIPE_SHADER_CAP_MAX_SAMPLER_VIEWS, + PIPE_SHADER_CAP_DOUBLES +}; + +/** + * Shader intermediate representation. + */ +enum pipe_shader_ir +{ + PIPE_SHADER_IR_TGSI, + PIPE_SHADER_IR_LLVM, + PIPE_SHADER_IR_NATIVE +}; + +/** + * Compute-specific implementation capability. They can be queried + * using pipe_screen::get_compute_param. + */ +enum pipe_compute_cap +{ + PIPE_COMPUTE_CAP_IR_TARGET, + PIPE_COMPUTE_CAP_GRID_DIMENSION, + PIPE_COMPUTE_CAP_MAX_GRID_SIZE, + PIPE_COMPUTE_CAP_MAX_BLOCK_SIZE, + PIPE_COMPUTE_CAP_MAX_THREADS_PER_BLOCK, + PIPE_COMPUTE_CAP_MAX_GLOBAL_SIZE, + PIPE_COMPUTE_CAP_MAX_LOCAL_SIZE, + PIPE_COMPUTE_CAP_MAX_PRIVATE_SIZE, + PIPE_COMPUTE_CAP_MAX_INPUT_SIZE, + PIPE_COMPUTE_CAP_MAX_MEM_ALLOC_SIZE, + PIPE_COMPUTE_CAP_MAX_CLOCK_FREQUENCY, + PIPE_COMPUTE_CAP_MAX_COMPUTE_UNITS, + PIPE_COMPUTE_CAP_IMAGES_SUPPORTED +}; + +/** + * Composite query types + */ + +/** + * Query result for PIPE_QUERY_SO_STATISTICS. + */ +struct pipe_query_data_so_statistics +{ + uint64_t num_primitives_written; + uint64_t primitives_storage_needed; +}; + +/** + * Query result for PIPE_QUERY_TIMESTAMP_DISJOINT. + */ +struct pipe_query_data_timestamp_disjoint +{ + uint64_t frequency; + boolean disjoint; +}; + +/** + * Query result for PIPE_QUERY_PIPELINE_STATISTICS. + */ +struct pipe_query_data_pipeline_statistics +{ + uint64_t ia_vertices; /**< Num vertices read by the vertex fetcher. */ + uint64_t ia_primitives; /**< Num primitives read by the vertex fetcher. */ + uint64_t vs_invocations; /**< Num vertex shader invocations. */ + uint64_t gs_invocations; /**< Num geometry shader invocations. */ + uint64_t gs_primitives; /**< Num primitives output by a geometry shader. */ + uint64_t c_invocations; /**< Num primitives sent to the rasterizer. */ + uint64_t c_primitives; /**< Num primitives that were rendered. */ + uint64_t ps_invocations; /**< Num pixel shader invocations. */ + uint64_t hs_invocations; /**< Num hull shader invocations. */ + uint64_t ds_invocations; /**< Num domain shader invocations. */ + uint64_t cs_invocations; /**< Num compute shader invocations. */ +}; + +/** + * Query result (returned by pipe_context::get_query_result). + */ +union pipe_query_result +{ + /* PIPE_QUERY_OCCLUSION_PREDICATE */ + /* PIPE_QUERY_SO_OVERFLOW_PREDICATE */ + /* PIPE_QUERY_GPU_FINISHED */ + boolean b; + + /* PIPE_QUERY_OCCLUSION_COUNTER */ + /* PIPE_QUERY_TIMESTAMP */ + /* PIPE_QUERY_TIME_ELAPSED */ + /* PIPE_QUERY_PRIMITIVES_GENERATED */ + /* PIPE_QUERY_PRIMITIVES_EMITTED */ + uint64_t u64; + + /* PIPE_QUERY_SO_STATISTICS */ + struct pipe_query_data_so_statistics so_statistics; + + /* PIPE_QUERY_TIMESTAMP_DISJOINT */ + struct pipe_query_data_timestamp_disjoint timestamp_disjoint; + + /* PIPE_QUERY_PIPELINE_STATISTICS */ + struct pipe_query_data_pipeline_statistics pipeline_statistics; +}; + +enum pipe_query_value_type +{ + PIPE_QUERY_TYPE_I32, + PIPE_QUERY_TYPE_U32, + PIPE_QUERY_TYPE_I64, + PIPE_QUERY_TYPE_U64, +}; + +union pipe_color_union +{ + float f[4]; + int i[4]; + unsigned int ui[4]; +}; + +struct pipe_driver_query_info +{ + const char *name; + unsigned query_type; /* PIPE_QUERY_DRIVER_SPECIFIC + i */ + uint64_t max_value; /* max value that can be returned */ + boolean uses_byte_units; /* whether the result is in bytes */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_format.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_format.h new file mode 100644 index 0000000..f9b7c67 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_format.h @@ -0,0 +1,494 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * Copyright (c) 2008 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef PIPE_FORMAT_H +#define PIPE_FORMAT_H + +#include "p_config.h" +#include "virgl_hw.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#define pipe_format virgl_formats +/** + * Formats for textures, surfaces and vertex data + */ +#define PIPE_FORMAT_NONE VIRGL_FORMAT_NONE +#define PIPE_FORMAT_B8G8R8A8_UNORM VIRGL_FORMAT_B8G8R8A8_UNORM +#define PIPE_FORMAT_B8G8R8X8_UNORM VIRGL_FORMAT_B8G8R8X8_UNORM +#define PIPE_FORMAT_A8R8G8B8_UNORM VIRGL_FORMAT_A8R8G8B8_UNORM +#define PIPE_FORMAT_X8R8G8B8_UNORM VIRGL_FORMAT_X8R8G8B8_UNORM +#define PIPE_FORMAT_B5G5R5A1_UNORM VIRGL_FORMAT_B5G5R5A1_UNORM +#define PIPE_FORMAT_B4G4R4A4_UNORM VIRGL_FORMAT_B4G4R4A4_UNORM +#define PIPE_FORMAT_B5G6R5_UNORM VIRGL_FORMAT_B5G6R5_UNORM +#define PIPE_FORMAT_R10G10B10A2_UNORM VIRGL_FORMAT_R10G10B10A2_UNORM +#define PIPE_FORMAT_L8_UNORM VIRGL_FORMAT_L8_UNORM /**< ubyte luminance */ +#define PIPE_FORMAT_A8_UNORM VIRGL_FORMAT_A8_UNORM /**< ubyte alpha */ +#define PIPE_FORMAT_I8_UNORM VIRGL_FORMAT_I8_UNORM /**< ubyte intensity */ +#define PIPE_FORMAT_L8A8_UNORM VIRGL_FORMAT_L8A8_UNORM /**< ubyte alpha, luminance */ +#define PIPE_FORMAT_L16_UNORM VIRGL_FORMAT_L16_UNORM /**< ushort luminance */ +#define PIPE_FORMAT_UYVY VIRGL_FORMAT_UYVY +#define PIPE_FORMAT_YUYV VIRGL_FORMAT_YUYV +#define PIPE_FORMAT_Z16_UNORM VIRGL_FORMAT_Z16_UNORM +#define PIPE_FORMAT_Z32_UNORM VIRGL_FORMAT_Z32_UNORM +#define PIPE_FORMAT_Z32_FLOAT VIRGL_FORMAT_Z32_FLOAT +#define PIPE_FORMAT_Z24_UNORM_S8_UINT VIRGL_FORMAT_Z24_UNORM_S8_UINT +#define PIPE_FORMAT_S8_UINT_Z24_UNORM VIRGL_FORMAT_S8_UINT_Z24_UNORM +#define PIPE_FORMAT_Z24X8_UNORM VIRGL_FORMAT_Z24X8_UNORM +#define PIPE_FORMAT_X8Z24_UNORM VIRGL_FORMAT_X8Z24_UNORM +#define PIPE_FORMAT_S8_UINT VIRGL_FORMAT_S8_UINT /**< ubyte stencil */ +#define PIPE_FORMAT_R64_FLOAT VIRGL_FORMAT_R64_FLOAT +#define PIPE_FORMAT_R64G64_FLOAT VIRGL_FORMAT_R64G64_FLOAT +#define PIPE_FORMAT_R64G64B64_FLOAT VIRGL_FORMAT_R64G64B64_FLOAT +#define PIPE_FORMAT_R64G64B64A64_FLOAT VIRGL_FORMAT_R64G64B64A64_FLOAT +#define PIPE_FORMAT_R32_FLOAT VIRGL_FORMAT_R32_FLOAT +#define PIPE_FORMAT_R32G32_FLOAT VIRGL_FORMAT_R32G32_FLOAT +#define PIPE_FORMAT_R32G32B32_FLOAT VIRGL_FORMAT_R32G32B32_FLOAT +#define PIPE_FORMAT_R32G32B32A32_FLOAT VIRGL_FORMAT_R32G32B32A32_FLOAT +#define PIPE_FORMAT_R32_UNORM VIRGL_FORMAT_R32_UNORM +#define PIPE_FORMAT_R32G32_UNORM VIRGL_FORMAT_R32G32_UNORM +#define PIPE_FORMAT_R32G32B32_UNORM VIRGL_FORMAT_R32G32B32_UNORM +#define PIPE_FORMAT_R32G32B32A32_UNORM VIRGL_FORMAT_R32G32B32A32_UNORM +#define PIPE_FORMAT_R32_USCALED VIRGL_FORMAT_R32_USCALED +#define PIPE_FORMAT_R32G32_USCALED VIRGL_FORMAT_R32G32_USCALED +#define PIPE_FORMAT_R32G32B32_USCALED VIRGL_FORMAT_R32G32B32_USCALED +#define PIPE_FORMAT_R32G32B32A32_USCALED VIRGL_FORMAT_R32G32B32A32_USCALED +#define PIPE_FORMAT_R32_SNORM VIRGL_FORMAT_R32_SNORM +#define PIPE_FORMAT_R32G32_SNORM VIRGL_FORMAT_R32G32_SNORM +#define PIPE_FORMAT_R32G32B32_SNORM VIRGL_FORMAT_R32G32B32_SNORM +#define PIPE_FORMAT_R32G32B32A32_SNORM VIRGL_FORMAT_R32G32B32A32_SNORM +#define PIPE_FORMAT_R32_SSCALED VIRGL_FORMAT_R32_SSCALED +#define PIPE_FORMAT_R32G32_SSCALED VIRGL_FORMAT_R32G32_SSCALED +#define PIPE_FORMAT_R32G32B32_SSCALED VIRGL_FORMAT_R32G32B32_SSCALED +#define PIPE_FORMAT_R32G32B32A32_SSCALED VIRGL_FORMAT_R32G32B32A32_SSCALED +#define PIPE_FORMAT_R16_UNORM VIRGL_FORMAT_R16_UNORM +#define PIPE_FORMAT_R16G16_UNORM VIRGL_FORMAT_R16G16_UNORM +#define PIPE_FORMAT_R16G16B16_UNORM VIRGL_FORMAT_R16G16B16_UNORM +#define PIPE_FORMAT_R16G16B16A16_UNORM VIRGL_FORMAT_R16G16B16A16_UNORM +#define PIPE_FORMAT_R16_USCALED VIRGL_FORMAT_R16_USCALED +#define PIPE_FORMAT_R16G16_USCALED VIRGL_FORMAT_R16G16_USCALED +#define PIPE_FORMAT_R16G16B16_USCALED VIRGL_FORMAT_R16G16B16_USCALED +#define PIPE_FORMAT_R16G16B16A16_USCALED VIRGL_FORMAT_R16G16B16A16_USCALED +#define PIPE_FORMAT_R16_SNORM VIRGL_FORMAT_R16_SNORM +#define PIPE_FORMAT_R16G16_SNORM VIRGL_FORMAT_R16G16_SNORM +#define PIPE_FORMAT_R16G16B16_SNORM VIRGL_FORMAT_R16G16B16_SNORM +#define PIPE_FORMAT_R16G16B16A16_SNORM VIRGL_FORMAT_R16G16B16A16_SNORM +#define PIPE_FORMAT_R16_SSCALED VIRGL_FORMAT_R16_SSCALED +#define PIPE_FORMAT_R16G16_SSCALED VIRGL_FORMAT_R16G16_SSCALED +#define PIPE_FORMAT_R16G16B16_SSCALED VIRGL_FORMAT_R16G16B16_SSCALED +#define PIPE_FORMAT_R16G16B16A16_SSCALED VIRGL_FORMAT_R16G16B16A16_SSCALED +#define PIPE_FORMAT_R8_UNORM VIRGL_FORMAT_R8_UNORM +#define PIPE_FORMAT_R8G8_UNORM VIRGL_FORMAT_R8G8_UNORM +#define PIPE_FORMAT_R8G8B8_UNORM VIRGL_FORMAT_R8G8B8_UNORM +#define PIPE_FORMAT_R8G8B8A8_UNORM VIRGL_FORMAT_R8G8B8A8_UNORM +#define PIPE_FORMAT_X8B8G8R8_UNORM VIRGL_FORMAT_X8B8G8R8_UNORM +#define PIPE_FORMAT_R8_USCALED VIRGL_FORMAT_R8_USCALED +#define PIPE_FORMAT_R8G8_USCALED VIRGL_FORMAT_R8G8_USCALED +#define PIPE_FORMAT_R8G8B8_USCALED VIRGL_FORMAT_R8G8B8_USCALED +#define PIPE_FORMAT_R8G8B8A8_USCALED VIRGL_FORMAT_R8G8B8A8_USCALED +#define PIPE_FORMAT_R8_SNORM VIRGL_FORMAT_R8_SNORM +#define PIPE_FORMAT_R8G8_SNORM VIRGL_FORMAT_R8G8_SNORM +#define PIPE_FORMAT_R8G8B8_SNORM VIRGL_FORMAT_R8G8B8_SNORM +#define PIPE_FORMAT_R8G8B8A8_SNORM VIRGL_FORMAT_R8G8B8A8_SNORM +#define PIPE_FORMAT_R8_SSCALED VIRGL_FORMAT_R8_SSCALED +#define PIPE_FORMAT_R8G8_SSCALED VIRGL_FORMAT_R8G8_SSCALED +#define PIPE_FORMAT_R8G8B8_SSCALED VIRGL_FORMAT_R8G8B8_SSCALED +#define PIPE_FORMAT_R8G8B8A8_SSCALED VIRGL_FORMAT_R8G8B8A8_SSCALED +#define PIPE_FORMAT_R32_FIXED VIRGL_FORMAT_R32_FIXED +#define PIPE_FORMAT_R32G32_FIXED VIRGL_FORMAT_R32G32_FIXED +#define PIPE_FORMAT_R32G32B32_FIXED VIRGL_FORMAT_R32G32B32_FIXED +#define PIPE_FORMAT_R32G32B32A32_FIXED VIRGL_FORMAT_R32G32B32A32_FIXED +#define PIPE_FORMAT_R16_FLOAT VIRGL_FORMAT_R16_FLOAT +#define PIPE_FORMAT_R16G16_FLOAT VIRGL_FORMAT_R16G16_FLOAT +#define PIPE_FORMAT_R16G16B16_FLOAT VIRGL_FORMAT_R16G16B16_FLOAT +#define PIPE_FORMAT_R16G16B16A16_FLOAT VIRGL_FORMAT_R16G16B16A16_FLOAT + +/* sRGB formats */ +#define PIPE_FORMAT_L8_SRGB VIRGL_FORMAT_L8_SRGB +#define PIPE_FORMAT_L8A8_SRGB VIRGL_FORMAT_L8A8_SRGB +#define PIPE_FORMAT_R8G8B8_SRGB VIRGL_FORMAT_R8G8B8_SRGB +#define PIPE_FORMAT_A8B8G8R8_SRGB VIRGL_FORMAT_A8B8G8R8_SRGB +#define PIPE_FORMAT_X8B8G8R8_SRGB VIRGL_FORMAT_X8B8G8R8_SRGB +#define PIPE_FORMAT_B8G8R8A8_SRGB VIRGL_FORMAT_B8G8R8A8_SRGB +#define PIPE_FORMAT_B8G8R8X8_SRGB VIRGL_FORMAT_B8G8R8X8_SRGB +#define PIPE_FORMAT_A8R8G8B8_SRGB VIRGL_FORMAT_A8R8G8B8_SRGB +#define PIPE_FORMAT_X8R8G8B8_SRGB VIRGL_FORMAT_X8R8G8B8_SRGB +#define PIPE_FORMAT_R8G8B8A8_SRGB VIRGL_FORMAT_R8G8B8A8_SRGB + +/* compressed formats */ +#define PIPE_FORMAT_DXT1_RGB VIRGL_FORMAT_DXT1_RGB +#define PIPE_FORMAT_DXT1_RGBA VIRGL_FORMAT_DXT1_RGBA +#define PIPE_FORMAT_DXT3_RGBA VIRGL_FORMAT_DXT3_RGBA +#define PIPE_FORMAT_DXT5_RGBA VIRGL_FORMAT_DXT5_RGBA + +/* sRGB, compressed */ +#define PIPE_FORMAT_DXT1_SRGB VIRGL_FORMAT_DXT1_SRGB +#define PIPE_FORMAT_DXT1_SRGBA VIRGL_FORMAT_DXT1_SRGBA +#define PIPE_FORMAT_DXT3_SRGBA VIRGL_FORMAT_DXT3_SRGBA +#define PIPE_FORMAT_DXT5_SRGBA VIRGL_FORMAT_DXT5_SRGBA + +/* rgtc compressed */ +#define PIPE_FORMAT_RGTC1_UNORM VIRGL_FORMAT_RGTC1_UNORM +#define PIPE_FORMAT_RGTC1_SNORM VIRGL_FORMAT_RGTC1_SNORM +#define PIPE_FORMAT_RGTC2_UNORM VIRGL_FORMAT_RGTC2_UNORM +#define PIPE_FORMAT_RGTC2_SNORM VIRGL_FORMAT_RGTC2_SNORM + +#define PIPE_FORMAT_R8G8_B8G8_UNORM VIRGL_FORMAT_R8G8_B8G8_UNORM +#define PIPE_FORMAT_G8R8_G8B8_UNORM VIRGL_FORMAT_G8R8_G8B8_UNORM + +/* mixed formats */ +#define PIPE_FORMAT_R8SG8SB8UX8U_NORM VIRGL_FORMAT_R8SG8SB8UX8U_NORM +#define PIPE_FORMAT_R5SG5SB6U_NORM VIRGL_FORMAT_R5SG5SB6U_NORM + +/* TODO: re-order these */ +#define PIPE_FORMAT_A8B8G8R8_UNORM VIRGL_FORMAT_A8B8G8R8_UNORM +#define PIPE_FORMAT_B5G5R5X1_UNORM VIRGL_FORMAT_B5G5R5X1_UNORM +#define PIPE_FORMAT_R10G10B10A2_USCALED VIRGL_FORMAT_R10G10B10A2_USCALED +#define PIPE_FORMAT_R11G11B10_FLOAT VIRGL_FORMAT_R11G11B10_FLOAT +#define PIPE_FORMAT_R9G9B9E5_FLOAT VIRGL_FORMAT_R9G9B9E5_FLOAT +#define PIPE_FORMAT_Z32_FLOAT_S8X24_UINT VIRGL_FORMAT_Z32_FLOAT_S8X24_UINT +#define PIPE_FORMAT_R1_UNORM VIRGL_FORMAT_R1_UNORM +#define PIPE_FORMAT_R10G10B10X2_USCALED VIRGL_FORMAT_R10G10B10X2_USCALED +#define PIPE_FORMAT_R10G10B10X2_SNORM VIRGL_FORMAT_R10G10B10X2_SNORM +#define PIPE_FORMAT_L4A4_UNORM VIRGL_FORMAT_L4A4_UNORM +#define PIPE_FORMAT_B10G10R10A2_UNORM VIRGL_FORMAT_B10G10R10A2_UNORM +#define PIPE_FORMAT_R10SG10SB10SA2U_NORM VIRGL_FORMAT_R10SG10SB10SA2U_NORM +#define PIPE_FORMAT_R8G8Bx_SNORM VIRGL_FORMAT_R8G8Bx_SNORM +#define PIPE_FORMAT_R8G8B8X8_UNORM VIRGL_FORMAT_R8G8B8X8_UNORM +#define PIPE_FORMAT_B4G4R4X4_UNORM VIRGL_FORMAT_B4G4R4X4_UNORM + +/* some stencil samplers formats */ +#define PIPE_FORMAT_X24S8_UINT VIRGL_FORMAT_X24S8_UINT +#define PIPE_FORMAT_S8X24_UINT VIRGL_FORMAT_S8X24_UINT +#define PIPE_FORMAT_X32_S8X24_UINT VIRGL_FORMAT_X32_S8X24_UINT + +#define PIPE_FORMAT_B2G3R3_UNORM VIRGL_FORMAT_B2G3R3_UNORM +#define PIPE_FORMAT_L16A16_UNORM VIRGL_FORMAT_L16A16_UNORM +#define PIPE_FORMAT_A16_UNORM VIRGL_FORMAT_A16_UNORM +#define PIPE_FORMAT_I16_UNORM VIRGL_FORMAT_I16_UNORM + +#define PIPE_FORMAT_LATC1_UNORM VIRGL_FORMAT_LATC1_UNORM +#define PIPE_FORMAT_LATC1_SNORM VIRGL_FORMAT_LATC1_SNORM +#define PIPE_FORMAT_LATC2_UNORM VIRGL_FORMAT_LATC2_UNORM +#define PIPE_FORMAT_LATC2_SNORM VIRGL_FORMAT_LATC2_SNORM + +#define PIPE_FORMAT_A8_SNORM VIRGL_FORMAT_A8_SNORM +#define PIPE_FORMAT_L8_SNORM VIRGL_FORMAT_L8_SNORM +#define PIPE_FORMAT_L8A8_SNORM VIRGL_FORMAT_L8A8_SNORM +#define PIPE_FORMAT_I8_SNORM VIRGL_FORMAT_I8_SNORM +#define PIPE_FORMAT_A16_SNORM VIRGL_FORMAT_A16_SNORM +#define PIPE_FORMAT_L16_SNORM VIRGL_FORMAT_L16_SNORM +#define PIPE_FORMAT_L16A16_SNORM VIRGL_FORMAT_L16A16_SNORM +#define PIPE_FORMAT_I16_SNORM VIRGL_FORMAT_I16_SNORM + +#define PIPE_FORMAT_A16_FLOAT VIRGL_FORMAT_A16_FLOAT +#define PIPE_FORMAT_L16_FLOAT VIRGL_FORMAT_L16_FLOAT +#define PIPE_FORMAT_L16A16_FLOAT VIRGL_FORMAT_L16A16_FLOAT +#define PIPE_FORMAT_I16_FLOAT VIRGL_FORMAT_I16_FLOAT +#define PIPE_FORMAT_A32_FLOAT VIRGL_FORMAT_A32_FLOAT +#define PIPE_FORMAT_L32_FLOAT VIRGL_FORMAT_L32_FLOAT +#define PIPE_FORMAT_L32A32_FLOAT VIRGL_FORMAT_L32A32_FLOAT +#define PIPE_FORMAT_I32_FLOAT VIRGL_FORMAT_I32_FLOAT + +#define PIPE_FORMAT_YV12 VIRGL_FORMAT_YV12 +#define PIPE_FORMAT_YV16 VIRGL_FORMAT_YV16 +#define PIPE_FORMAT_IYUV VIRGL_FORMAT_IYUV /**< aka I420 */ +#define PIPE_FORMAT_NV12 VIRGL_FORMAT_NV12 +#define PIPE_FORMAT_NV21 VIRGL_FORMAT_NV21 + +#define PIPE_FORMAT_A4R4_UNORM VIRGL_FORMAT_A4R4_UNORM +#define PIPE_FORMAT_R4A4_UNORM VIRGL_FORMAT_R4A4_UNORM +#define PIPE_FORMAT_R8A8_UNORM VIRGL_FORMAT_R8A8_UNORM +#define PIPE_FORMAT_A8R8_UNORM VIRGL_FORMAT_A8R8_UNORM + +#define PIPE_FORMAT_R10G10B10A2_SSCALED VIRGL_FORMAT_R10G10B10A2_SSCALED +#define PIPE_FORMAT_R10G10B10A2_SNORM VIRGL_FORMAT_R10G10B10A2_SNORM + +#define PIPE_FORMAT_B10G10R10A2_USCALED VIRGL_FORMAT_B10G10R10A2_USCALED +#define PIPE_FORMAT_B10G10R10A2_SSCALED VIRGL_FORMAT_B10G10R10A2_SSCALED +#define PIPE_FORMAT_B10G10R10A2_SNORM VIRGL_FORMAT_B10G10R10A2_SNORM + +#define PIPE_FORMAT_R8_UINT VIRGL_FORMAT_R8_UINT +#define PIPE_FORMAT_R8G8_UINT VIRGL_FORMAT_R8G8_UINT +#define PIPE_FORMAT_R8G8B8_UINT VIRGL_FORMAT_R8G8B8_UINT +#define PIPE_FORMAT_R8G8B8A8_UINT VIRGL_FORMAT_R8G8B8A8_UINT + +#define PIPE_FORMAT_R8_SINT VIRGL_FORMAT_R8_SINT +#define PIPE_FORMAT_R8G8_SINT VIRGL_FORMAT_R8G8_SINT +#define PIPE_FORMAT_R8G8B8_SINT VIRGL_FORMAT_R8G8B8_SINT +#define PIPE_FORMAT_R8G8B8A8_SINT VIRGL_FORMAT_R8G8B8A8_SINT + +#define PIPE_FORMAT_R16_UINT VIRGL_FORMAT_R16_UINT +#define PIPE_FORMAT_R16G16_UINT VIRGL_FORMAT_R16G16_UINT +#define PIPE_FORMAT_R16G16B16_UINT VIRGL_FORMAT_R16G16B16_UINT +#define PIPE_FORMAT_R16G16B16A16_UINT VIRGL_FORMAT_R16G16B16A16_UINT + +#define PIPE_FORMAT_R16_SINT VIRGL_FORMAT_R16_SINT +#define PIPE_FORMAT_R16G16_SINT VIRGL_FORMAT_R16G16_SINT +#define PIPE_FORMAT_R16G16B16_SINT VIRGL_FORMAT_R16G16B16_SINT +#define PIPE_FORMAT_R16G16B16A16_SINT VIRGL_FORMAT_R16G16B16A16_SINT + +#define PIPE_FORMAT_R32_UINT VIRGL_FORMAT_R32_UINT +#define PIPE_FORMAT_R32G32_UINT VIRGL_FORMAT_R32G32_UINT +#define PIPE_FORMAT_R32G32B32_UINT VIRGL_FORMAT_R32G32B32_UINT +#define PIPE_FORMAT_R32G32B32A32_UINT VIRGL_FORMAT_R32G32B32A32_UINT + +#define PIPE_FORMAT_R32_SINT VIRGL_FORMAT_R32_SINT +#define PIPE_FORMAT_R32G32_SINT VIRGL_FORMAT_R32G32_SINT +#define PIPE_FORMAT_R32G32B32_SINT VIRGL_FORMAT_R32G32B32_SINT +#define PIPE_FORMAT_R32G32B32A32_SINT VIRGL_FORMAT_R32G32B32A32_SINT + +#define PIPE_FORMAT_A8_UINT VIRGL_FORMAT_A8_UINT +#define PIPE_FORMAT_I8_UINT VIRGL_FORMAT_I8_UINT +#define PIPE_FORMAT_L8_UINT VIRGL_FORMAT_L8_UINT +#define PIPE_FORMAT_L8A8_UINT VIRGL_FORMAT_L8A8_UINT + +#define PIPE_FORMAT_A8_SINT VIRGL_FORMAT_A8_SINT +#define PIPE_FORMAT_I8_SINT VIRGL_FORMAT_I8_SINT +#define PIPE_FORMAT_L8_SINT VIRGL_FORMAT_L8_SINT +#define PIPE_FORMAT_L8A8_SINT VIRGL_FORMAT_L8A8_SINT + +#define PIPE_FORMAT_A16_UINT VIRGL_FORMAT_A16_UINT +#define PIPE_FORMAT_I16_UINT VIRGL_FORMAT_I16_UINT +#define PIPE_FORMAT_L16_UINT VIRGL_FORMAT_L16_UINT +#define PIPE_FORMAT_L16A16_UINT VIRGL_FORMAT_L16A16_UINT + +#define PIPE_FORMAT_A16_SINT VIRGL_FORMAT_A16_SINT +#define PIPE_FORMAT_I16_SINT VIRGL_FORMAT_I16_SINT +#define PIPE_FORMAT_L16_SINT VIRGL_FORMAT_L16_SINT +#define PIPE_FORMAT_L16A16_SINT VIRGL_FORMAT_L16A16_SINT + +#define PIPE_FORMAT_A32_UINT VIRGL_FORMAT_A32_UINT +#define PIPE_FORMAT_I32_UINT VIRGL_FORMAT_I32_UINT +#define PIPE_FORMAT_L32_UINT VIRGL_FORMAT_L32_UINT +#define PIPE_FORMAT_L32A32_UINT VIRGL_FORMAT_L32A32_UINT + +#define PIPE_FORMAT_A32_SINT VIRGL_FORMAT_A32_SINT +#define PIPE_FORMAT_I32_SINT VIRGL_FORMAT_I32_SINT +#define PIPE_FORMAT_L32_SINT VIRGL_FORMAT_L32_SINT +#define PIPE_FORMAT_L32A32_SINT VIRGL_FORMAT_L32A32_SINT + +#define PIPE_FORMAT_B10G10R10A2_UINT VIRGL_FORMAT_B10G10R10A2_UINT + +#define PIPE_FORMAT_ETC1_RGB8 VIRGL_FORMAT_ETC1_RGB8 + +#define PIPE_FORMAT_R8G8_R8B8_UNORM VIRGL_FORMAT_R8G8_R8B8_UNORM +#define PIPE_FORMAT_G8R8_B8R8_UNORM VIRGL_FORMAT_G8R8_B8R8_UNORM + +#define PIPE_FORMAT_R8G8B8X8_SNORM VIRGL_FORMAT_R8G8B8X8_SNORM +#define PIPE_FORMAT_R8G8B8X8_SRGB VIRGL_FORMAT_R8G8B8X8_SRGB +#define PIPE_FORMAT_R8G8B8X8_UINT VIRGL_FORMAT_R8G8B8X8_UINT +#define PIPE_FORMAT_R8G8B8X8_SINT VIRGL_FORMAT_R8G8B8X8_SINT +#define PIPE_FORMAT_B10G10R10X2_UNORM VIRGL_FORMAT_B10G10R10X2_UNORM +#define PIPE_FORMAT_R16G16B16X16_UNORM VIRGL_FORMAT_R16G16B16X16_UNORM +#define PIPE_FORMAT_R16G16B16X16_SNORM VIRGL_FORMAT_R16G16B16X16_SNORM +#define PIPE_FORMAT_R16G16B16X16_FLOAT VIRGL_FORMAT_R16G16B16X16_FLOAT +#define PIPE_FORMAT_R16G16B16X16_UINT VIRGL_FORMAT_R16G16B16X16_UINT +#define PIPE_FORMAT_R16G16B16X16_SINT VIRGL_FORMAT_R16G16B16X16_SINT +#define PIPE_FORMAT_R32G32B32X32_FLOAT VIRGL_FORMAT_R32G32B32X32_FLOAT +#define PIPE_FORMAT_R32G32B32X32_UINT VIRGL_FORMAT_R32G32B32X32_UINT +#define PIPE_FORMAT_R32G32B32X32_SINT VIRGL_FORMAT_R32G32B32X32_SINT + +#define PIPE_FORMAT_R8A8_SNORM VIRGL_FORMAT_R8A8_SNORM +#define PIPE_FORMAT_R16A16_UNORM VIRGL_FORMAT_R16A16_UNORM +#define PIPE_FORMAT_R16A16_SNORM VIRGL_FORMAT_R16A16_SNORM +#define PIPE_FORMAT_R16A16_FLOAT VIRGL_FORMAT_R16A16_FLOAT +#define PIPE_FORMAT_R32A32_FLOAT VIRGL_FORMAT_R32A32_FLOAT +#define PIPE_FORMAT_R8A8_UINT VIRGL_FORMAT_R8A8_UINT +#define PIPE_FORMAT_R8A8_SINT VIRGL_FORMAT_R8A8_SINT +#define PIPE_FORMAT_R16A16_UINT VIRGL_FORMAT_R16A16_UINT +#define PIPE_FORMAT_R16A16_SINT VIRGL_FORMAT_R16A16_SINT +#define PIPE_FORMAT_R32A32_UINT VIRGL_FORMAT_R32A32_UINT +#define PIPE_FORMAT_R32A32_SINT VIRGL_FORMAT_R32A32_SINT +#define PIPE_FORMAT_R10G10B10A2_UINT VIRGL_FORMAT_R10G10B10A2_UINT + +#define PIPE_FORMAT_B5G6R5_SRGB VIRGL_FORMAT_B5G6R5_SRGB + +#define PIPE_FORMAT_BPTC_RGBA_UNORM VIRGL_FORMAT_BPTC_RGBA_UNORM +#define PIPE_FORMAT_BPTC_SRGBA VIRGL_FORMAT_BPTC_SRGBA +#define PIPE_FORMAT_BPTC_RGB_FLOAT VIRGL_FORMAT_BPTC_RGB_FLOAT +#define PIPE_FORMAT_BPTC_RGB_UFLOAT VIRGL_FORMAT_BPTC_RGB_UFLOAT + +#define PIPE_FORMAT_A8L8_UNORM VIRGL_FORMAT_A8L8_UNORM +#define PIPE_FORMAT_A8L8_SNORM VIRGL_FORMAT_A8L8_SNORM +#define PIPE_FORMAT_A8L8_SRGB VIRGL_FORMAT_A8L8_SRGB +#define PIPE_FORMAT_A16L16_UNORM VIRGL_FORMAT_A16L16_UNORM + +#define PIPE_FORMAT_G8R8_UNORM VIRGL_FORMAT_G8R8_UNORM +#define PIPE_FORMAT_G8R8_SNORM VIRGL_FORMAT_G8R8_SNORM +#define PIPE_FORMAT_G16R16_UNORM VIRGL_FORMAT_G16R16_UNORM +#define PIPE_FORMAT_G16R16_SNORM VIRGL_FORMAT_G16R16_SNORM + +#define PIPE_FORMAT_A8B8G8R8_SNORM VIRGL_FORMAT_A8B8G8R8_SNORM +#define PIPE_FORMAT_X8B8G8R8_SNORM VIRGL_FORMAT_X8B8G8R8_SNORM + +#define PIPE_FORMAT_ETC2_RGB8 VIRGL_FORMAT_ETC2_RGB8 +#define PIPE_FORMAT_ETC2_SRGB8 VIRGL_FORMAT_ETC2_SRGB8 +#define PIPE_FORMAT_ETC2_RGB8A1 VIRGL_FORMAT_ETC2_RGB8A1 +#define PIPE_FORMAT_ETC2_SRGB8A1 VIRGL_FORMAT_ETC2_SRGB8A1 +#define PIPE_FORMAT_ETC2_RGBA8 VIRGL_FORMAT_ETC2_RGBA8 +#define PIPE_FORMAT_ETC2_SRGBA8 VIRGL_FORMAT_ETC2_SRGBA8 +#define PIPE_FORMAT_ETC2_R11_UNORM VIRGL_FORMAT_ETC2_R11_UNORM +#define PIPE_FORMAT_ETC2_R11_SNORM VIRGL_FORMAT_ETC2_R11_SNORM +#define PIPE_FORMAT_ETC2_RG11_UNORM VIRGL_FORMAT_ETC2_RG11_UNORM +#define PIPE_FORMAT_ETC2_RG11_SNORM VIRGL_FORMAT_ETC2_RG11_SNORM + +#define PIPE_FORMAT_ASTC_4x4 VIRGL_FORMAT_ASTC_4x4 +#define PIPE_FORMAT_ASTC_5x4 VIRGL_FORMAT_ASTC_5x4 +#define PIPE_FORMAT_ASTC_5x5 VIRGL_FORMAT_ASTC_5x5 +#define PIPE_FORMAT_ASTC_6x5 VIRGL_FORMAT_ASTC_6x5 +#define PIPE_FORMAT_ASTC_6x6 VIRGL_FORMAT_ASTC_6x6 +#define PIPE_FORMAT_ASTC_8x5 VIRGL_FORMAT_ASTC_8x5 +#define PIPE_FORMAT_ASTC_8x6 VIRGL_FORMAT_ASTC_8x6 +#define PIPE_FORMAT_ASTC_8x8 VIRGL_FORMAT_ASTC_8x8 +#define PIPE_FORMAT_ASTC_10x5 VIRGL_FORMAT_ASTC_10x5 +#define PIPE_FORMAT_ASTC_10x6 VIRGL_FORMAT_ASTC_10x6 +#define PIPE_FORMAT_ASTC_10x8 VIRGL_FORMAT_ASTC_10x8 +#define PIPE_FORMAT_ASTC_10x10 VIRGL_FORMAT_ASTC_10x10 +#define PIPE_FORMAT_ASTC_12x10 VIRGL_FORMAT_ASTC_12x10 +#define PIPE_FORMAT_ASTC_12x12 VIRGL_FORMAT_ASTC_12x12 + +#define PIPE_FORMAT_ASTC_4x4_SRGB VIRGL_FORMAT_ASTC_4x4_SRGB +#define PIPE_FORMAT_ASTC_5x4_SRGB VIRGL_FORMAT_ASTC_5x4_SRGB +#define PIPE_FORMAT_ASTC_5x5_SRGB VIRGL_FORMAT_ASTC_5x5_SRGB +#define PIPE_FORMAT_ASTC_6x5_SRGB VIRGL_FORMAT_ASTC_6x5_SRGB +#define PIPE_FORMAT_ASTC_6x6_SRGB VIRGL_FORMAT_ASTC_6x6_SRGB +#define PIPE_FORMAT_ASTC_8x5_SRGB VIRGL_FORMAT_ASTC_8x5_SRGB +#define PIPE_FORMAT_ASTC_8x6_SRGB VIRGL_FORMAT_ASTC_8x6_SRGB +#define PIPE_FORMAT_ASTC_8x8_SRGB VIRGL_FORMAT_ASTC_8x8_SRGB +#define PIPE_FORMAT_ASTC_10x5_SRGB VIRGL_FORMAT_ASTC_10x5_SRGB +#define PIPE_FORMAT_ASTC_10x6_SRGB VIRGL_FORMAT_ASTC_10x6_SRGB +#define PIPE_FORMAT_ASTC_10x8_SRGB VIRGL_FORMAT_ASTC_10x8_SRGB +#define PIPE_FORMAT_ASTC_10x10_SRGB VIRGL_FORMAT_ASTC_10x10_SRGB +#define PIPE_FORMAT_ASTC_12x10_SRGB VIRGL_FORMAT_ASTC_12x10_SRGB +#define PIPE_FORMAT_ASTC_12x12_SRGB VIRGL_FORMAT_ASTC_12x12_SRGB + +#define PIPE_FORMAT_P016 VIRGL_FORMAT_P016 + +#define PIPE_FORMAT_R10G10B10X2_UNORM VIRGL_FORMAT_R10G10B10X2_UNORM +#define PIPE_FORMAT_A1B5G5R5_UNORM VIRGL_FORMAT_A1B5G5R5_UNORM +#define PIPE_FORMAT_X1B5G5R5_UNORM VIRGL_FORMAT_X1B5G5R5_UNORM +#define PIPE_FORMAT_A4B4G4R4_UNORM VIRGL_FORMAT_A4B4G4R4_UNORM + +#define PIPE_FORMAT_R8_SRGB VIRGL_FORMAT_R8_SRGB + +#define PIPE_FORMAT_COUNT VIRGL_FORMAT_MAX + +#if defined(PIPE_ARCH_LITTLE_ENDIAN) +#define PIPE_FORMAT_RGBA8888_UNORM PIPE_FORMAT_R8G8B8A8_UNORM +#define PIPE_FORMAT_RGBX8888_UNORM PIPE_FORMAT_R8G8B8X8_UNORM +#define PIPE_FORMAT_BGRA8888_UNORM PIPE_FORMAT_B8G8R8A8_UNORM +#define PIPE_FORMAT_BGRX8888_UNORM PIPE_FORMAT_B8G8R8X8_UNORM +#define PIPE_FORMAT_ARGB8888_UNORM PIPE_FORMAT_A8R8G8B8_UNORM +#define PIPE_FORMAT_XRGB8888_UNORM PIPE_FORMAT_X8R8G8B8_UNORM +#define PIPE_FORMAT_ABGR8888_UNORM PIPE_FORMAT_A8B8G8R8_UNORM +#define PIPE_FORMAT_XBGR8888_UNORM PIPE_FORMAT_X8B8G8R8_UNORM +#define PIPE_FORMAT_RGBA8888_SNORM PIPE_FORMAT_R8G8B8A8_SNORM +#define PIPE_FORMAT_RGBX8888_SNORM PIPE_FORMAT_R8G8B8X8_SNORM +#define PIPE_FORMAT_ABGR8888_SNORM PIPE_FORMAT_A8B8G8R8_SNORM +#define PIPE_FORMAT_XBGR8888_SNORM PIPE_FORMAT_X8B8G8R8_SNORM +#define PIPE_FORMAT_RGBA8888_SRGB PIPE_FORMAT_R8G8B8A8_SRGB +#define PIPE_FORMAT_RGBX8888_SRGB PIPE_FORMAT_R8G8B8X8_SRGB +#define PIPE_FORMAT_BGRA8888_SRGB PIPE_FORMAT_B8G8R8A8_SRGB +#define PIPE_FORMAT_BGRX8888_SRGB PIPE_FORMAT_B8G8R8X8_SRGB +#define PIPE_FORMAT_ARGB8888_SRGB PIPE_FORMAT_A8R8G8B8_SRGB +#define PIPE_FORMAT_XRGB8888_SRGB PIPE_FORMAT_X8R8G8B8_SRGB +#define PIPE_FORMAT_ABGR8888_SRGB PIPE_FORMAT_A8B8G8R8_SRGB +#define PIPE_FORMAT_XBGR8888_SRGB PIPE_FORMAT_X8B8G8R8_SRGB +#define PIPE_FORMAT_LA88_UNORM PIPE_FORMAT_L8A8_UNORM +#define PIPE_FORMAT_AL88_UNORM PIPE_FORMAT_A8L8_UNORM +#define PIPE_FORMAT_LA88_SNORM PIPE_FORMAT_L8A8_SNORM +#define PIPE_FORMAT_AL88_SNORM PIPE_FORMAT_A8L8_SNORM +#define PIPE_FORMAT_LA88_SRGB PIPE_FORMAT_L8A8_SRGB +#define PIPE_FORMAT_AL88_SRGB PIPE_FORMAT_A8L8_SRGB +#define PIPE_FORMAT_LA1616_UNORM PIPE_FORMAT_L16A16_UNORM +#define PIPE_FORMAT_AL1616_UNORM PIPE_FORMAT_A16L16_UNORM +#define PIPE_FORMAT_RG88_UNORM PIPE_FORMAT_R8G8_UNORM +#define PIPE_FORMAT_GR88_UNORM PIPE_FORMAT_G8R8_UNORM +#define PIPE_FORMAT_RG88_SNORM PIPE_FORMAT_R8G8_SNORM +#define PIPE_FORMAT_GR88_SNORM PIPE_FORMAT_G8R8_SNORM +#define PIPE_FORMAT_RG1616_UNORM PIPE_FORMAT_R16G16_UNORM +#define PIPE_FORMAT_GR1616_UNORM PIPE_FORMAT_G16R16_UNORM +#define PIPE_FORMAT_RG1616_SNORM PIPE_FORMAT_R16G16_SNORM +#define PIPE_FORMAT_GR1616_SNORM PIPE_FORMAT_G16R16_SNORM +#elif defined(PIPE_ARCH_BIG_ENDIAN) +#define PIPE_FORMAT_ABGR8888_UNORM PIPE_FORMAT_R8G8B8A8_UNORM +#define PIPE_FORMAT_XBGR8888_UNORM PIPE_FORMAT_R8G8B8X8_UNORM +#define PIPE_FORMAT_ARGB8888_UNORM PIPE_FORMAT_B8G8R8A8_UNORM +#define PIPE_FORMAT_XRGB8888_UNORM PIPE_FORMAT_B8G8R8X8_UNORM +#define PIPE_FORMAT_BGRA8888_UNORM PIPE_FORMAT_A8R8G8B8_UNORM +#define PIPE_FORMAT_BGRX8888_UNORM PIPE_FORMAT_X8R8G8B8_UNORM +#define PIPE_FORMAT_RGBA8888_UNORM PIPE_FORMAT_A8B8G8R8_UNORM +#define PIPE_FORMAT_RGBX8888_UNORM PIPE_FORMAT_X8B8G8R8_UNORM +#define PIPE_FORMAT_ABGR8888_SNORM PIPE_FORMAT_R8G8B8A8_SNORM +#define PIPE_FORMAT_XBGR8888_SNORM PIPE_FORMAT_R8G8B8X8_SNORM +#define PIPE_FORMAT_RGBA8888_SNORM PIPE_FORMAT_A8B8G8R8_SNORM +#define PIPE_FORMAT_RGBX8888_SNORM PIPE_FORMAT_X8B8G8R8_SNORM +#define PIPE_FORMAT_ABGR8888_SRGB PIPE_FORMAT_R8G8B8A8_SRGB +#define PIPE_FORMAT_XBGR8888_SRGB PIPE_FORMAT_R8G8B8X8_SRGB +#define PIPE_FORMAT_ARGB8888_SRGB PIPE_FORMAT_B8G8R8A8_SRGB +#define PIPE_FORMAT_XRGB8888_SRGB PIPE_FORMAT_B8G8R8X8_SRGB +#define PIPE_FORMAT_BGRA8888_SRGB PIPE_FORMAT_A8R8G8B8_SRGB +#define PIPE_FORMAT_BGRX8888_SRGB PIPE_FORMAT_X8R8G8B8_SRGB +#define PIPE_FORMAT_RGBA8888_SRGB PIPE_FORMAT_A8B8G8R8_SRGB +#define PIPE_FORMAT_RGBX8888_SRGB PIPE_FORMAT_X8B8G8R8_SRGB +#define PIPE_FORMAT_LA88_UNORM PIPE_FORMAT_A8L8_UNORM +#define PIPE_FORMAT_AL88_UNORM PIPE_FORMAT_L8A8_UNORM +#define PIPE_FORMAT_LA88_SNORM PIPE_FORMAT_A8L8_SNORM +#define PIPE_FORMAT_AL88_SNORM PIPE_FORMAT_L8A8_SNORM +#define PIPE_FORMAT_LA88_SRGB PIPE_FORMAT_A8L8_SRGB +#define PIPE_FORMAT_AL88_SRGB PIPE_FORMAT_L8A8_SRGB +#define PIPE_FORMAT_LA1616_UNORM PIPE_FORMAT_A16L16_UNORM +#define PIPE_FORMAT_AL1616_UNORM PIPE_FORMAT_L16A16_UNORM +#define PIPE_FORMAT_RG88_UNORM PIPE_FORMAT_G8R8_UNORM +#define PIPE_FORMAT_GR88_UNORM PIPE_FORMAT_R8G8_UNORM +#define PIPE_FORMAT_RG88_SNORM PIPE_FORMAT_G8R8_SNORM +#define PIPE_FORMAT_GR88_SNORM PIPE_FORMAT_R8G8_SNORM +#define PIPE_FORMAT_RG1616_UNORM PIPE_FORMAT_G16R16_UNORM +#define PIPE_FORMAT_GR1616_UNORM PIPE_FORMAT_R16G16_UNORM +#define PIPE_FORMAT_RG1616_SNORM PIPE_FORMAT_G16R16_SNORM +#define PIPE_FORMAT_GR1616_SNORM PIPE_FORMAT_R16G16_SNORM +#endif + +enum pipe_video_chroma_format +{ + PIPE_VIDEO_CHROMA_FORMAT_400, + PIPE_VIDEO_CHROMA_FORMAT_420, + PIPE_VIDEO_CHROMA_FORMAT_422, + PIPE_VIDEO_CHROMA_FORMAT_444, + PIPE_VIDEO_CHROMA_FORMAT_NONE +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_screen.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_screen.h new file mode 100644 index 0000000..cf958d2 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_screen.h @@ -0,0 +1,230 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/** + * @file + * + * Screen, Adapter or GPU + * + * These are driver functions/facilities that are context independent. + */ + + +#ifndef P_SCREEN_H +#define P_SCREEN_H + + +#include "pipe/p_compiler.h" +#include "pipe/p_format.h" +#include "pipe/p_defines.h" +#include "pipe/p_video_enums.h" + + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** Opaque types */ +struct winsys_handle; +struct pipe_fence_handle; +struct pipe_resource; +struct pipe_surface; +struct pipe_transfer; +struct pipe_box; + + +/** + * Gallium screen/adapter context. Basically everything + * hardware-specific that doesn't actually require a rendering + * context. + */ +struct pipe_screen { + void (*destroy)( struct pipe_screen * ); + + const char *(*get_name)( struct pipe_screen * ); + + const char *(*get_vendor)( struct pipe_screen * ); + + /** + * Query an integer-valued capability/parameter/limit + * \param param one of PIPE_CAP_x + */ + int (*get_param)( struct pipe_screen *, enum pipe_cap param ); + + /** + * Query a float-valued capability/parameter/limit + * \param param one of PIPE_CAP_x + */ + float (*get_paramf)( struct pipe_screen *, enum pipe_capf param ); + + /** + * Query a per-shader-stage integer-valued capability/parameter/limit + * \param param one of PIPE_CAP_x + */ + int (*get_shader_param)( struct pipe_screen *, unsigned shader, enum pipe_shader_cap param ); + + /** + * Query an integer-valued capability/parameter/limit for a codec/profile + * \param param one of PIPE_VIDEO_CAP_x + */ + int (*get_video_param)( struct pipe_screen *, + enum pipe_video_profile profile, + enum pipe_video_entrypoint entrypoint, + enum pipe_video_cap param ); + + /** + * Query a compute-specific capability/parameter/limit. + * \param param one of PIPE_COMPUTE_CAP_x + * \param ret pointer to a preallocated buffer that will be + * initialized to the parameter value, or NULL. + * \return size in bytes of the parameter value that would be + * returned. + */ + int (*get_compute_param)(struct pipe_screen *, + enum pipe_compute_cap param, + void *ret); + + /** + * Query a timestamp in nanoseconds. The returned value should match + * PIPE_QUERY_TIMESTAMP. This function returns immediately and doesn't + * wait for rendering to complete (which cannot be achieved with queries). + */ + uint64_t (*get_timestamp)(struct pipe_screen *); + + struct pipe_context * (*context_create)( struct pipe_screen *, + void *priv ); + + /** + * Check if the given pipe_format is supported as a texture or + * drawing surface. + * \param bindings bitmask of PIPE_BIND_* + */ + boolean (*is_format_supported)( struct pipe_screen *, + enum pipe_format format, + enum pipe_texture_target target, + unsigned sample_count, + unsigned bindings ); + + /** + * Check if the given pipe_format is supported as output for this codec/profile. + * \param profile profile to check, may also be PIPE_VIDEO_PROFILE_UNKNOWN + */ + boolean (*is_video_format_supported)( struct pipe_screen *, + enum pipe_format format, + enum pipe_video_profile profile, + enum pipe_video_entrypoint entrypoint ); + + /** + * Check if we can actually create the given resource (test the dimension, + * overall size, etc). Used to implement proxy textures. + * \return TRUE if size is OK, FALSE if too large. + */ + boolean (*can_create_resource)(struct pipe_screen *screen, + const struct pipe_resource *templat); + + /** + * Create a new texture object, using the given template info. + */ + struct pipe_resource * (*resource_create)(struct pipe_screen *, + const struct pipe_resource *templat); + + /** + * Create a texture from a winsys_handle. The handle is often created in + * another process by first creating a pipe texture and then calling + * resource_get_handle. + */ + struct pipe_resource * (*resource_from_handle)(struct pipe_screen *, + const struct pipe_resource *templat, + struct winsys_handle *handle); + + /** + * Get a winsys_handle from a texture. Some platforms/winsys requires + * that the texture is created with a special usage flag like + * DISPLAYTARGET or PRIMARY. + */ + boolean (*resource_get_handle)(struct pipe_screen *, + struct pipe_resource *tex, + struct winsys_handle *handle); + + + void (*resource_destroy)(struct pipe_screen *, + struct pipe_resource *pt); + + + /** + * Do any special operations to ensure frontbuffer contents are + * displayed, eg copy fake frontbuffer. + * \param winsys_drawable_handle an opaque handle that the calling context + * gets out-of-band + * \param subbox an optional sub region to flush + */ + void (*flush_frontbuffer)( struct pipe_screen *screen, + struct pipe_resource *resource, + unsigned level, unsigned layer, + void *winsys_drawable_handle, + struct pipe_box *subbox ); + + /** Set ptr = fence, with reference counting */ + void (*fence_reference)( struct pipe_screen *screen, + struct pipe_fence_handle **ptr, + struct pipe_fence_handle *fence ); + + /** + * Checks whether the fence has been signalled. + */ + boolean (*fence_signalled)( struct pipe_screen *screen, + struct pipe_fence_handle *fence ); + + /** + * Wait for the fence to finish. + * \param timeout in nanoseconds (may be PIPE_TIMEOUT_INFINITE). + */ + boolean (*fence_finish)( struct pipe_screen *screen, + struct pipe_fence_handle *fence, + uint64_t timeout ); + + /** + * Returns a driver-specific query. + * + * If \p info is NULL, the number of available queries is returned. + * Otherwise, the driver query at the specified \p index is returned + * in \p info. The function returns non-zero on success. + */ + int (*get_driver_query_info)(struct pipe_screen *screen, + unsigned index, + struct pipe_driver_query_info *info); + +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* P_SCREEN_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_shader_tokens.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_shader_tokens.h new file mode 100644 index 0000000..1645082 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_shader_tokens.h @@ -0,0 +1,800 @@ +/************************************************************************** + * + * Copyright 2008 VMware, Inc. + * Copyright 2009-2010 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef P_SHADER_TOKENS_H +#define P_SHADER_TOKENS_H + +#ifdef __cplusplus +extern "C" { +#endif + + +struct tgsi_header +{ + unsigned HeaderSize : 8; + unsigned BodySize : 24; +}; + +#define TGSI_PROCESSOR_FRAGMENT 0 +#define TGSI_PROCESSOR_VERTEX 1 +#define TGSI_PROCESSOR_GEOMETRY 2 +#define TGSI_PROCESSOR_TESS_CTRL 3 +#define TGSI_PROCESSOR_TESS_EVAL 4 +#define TGSI_PROCESSOR_COMPUTE 5 + +struct tgsi_processor +{ + unsigned Processor : 4; /* TGSI_PROCESSOR_ */ + unsigned Padding : 28; +}; + +#define TGSI_TOKEN_TYPE_DECLARATION 0 +#define TGSI_TOKEN_TYPE_IMMEDIATE 1 +#define TGSI_TOKEN_TYPE_INSTRUCTION 2 +#define TGSI_TOKEN_TYPE_PROPERTY 3 + +struct tgsi_token +{ + unsigned Type : 4; /**< TGSI_TOKEN_TYPE_x */ + unsigned NrTokens : 8; /**< UINT */ + unsigned Padding : 20; +}; + +enum tgsi_file_type { + TGSI_FILE_NULL =0, + TGSI_FILE_CONSTANT =1, + TGSI_FILE_INPUT =2, + TGSI_FILE_OUTPUT =3, + TGSI_FILE_TEMPORARY =4, + TGSI_FILE_SAMPLER =5, + TGSI_FILE_ADDRESS =6, + TGSI_FILE_IMMEDIATE =7, + TGSI_FILE_PREDICATE =8, + TGSI_FILE_SYSTEM_VALUE =9, + TGSI_FILE_IMAGE =10, + TGSI_FILE_SAMPLER_VIEW =11, + TGSI_FILE_BUFFER, + TGSI_FILE_MEMORY, + TGSI_FILE_HW_ATOMIC, + TGSI_FILE_COUNT /**< how many TGSI_FILE_ types */ +}; + + +#define TGSI_WRITEMASK_NONE 0x00 +#define TGSI_WRITEMASK_X 0x01 +#define TGSI_WRITEMASK_Y 0x02 +#define TGSI_WRITEMASK_XY 0x03 +#define TGSI_WRITEMASK_Z 0x04 +#define TGSI_WRITEMASK_XZ 0x05 +#define TGSI_WRITEMASK_YZ 0x06 +#define TGSI_WRITEMASK_XYZ 0x07 +#define TGSI_WRITEMASK_W 0x08 +#define TGSI_WRITEMASK_XW 0x09 +#define TGSI_WRITEMASK_YW 0x0A +#define TGSI_WRITEMASK_XYW 0x0B +#define TGSI_WRITEMASK_ZW 0x0C +#define TGSI_WRITEMASK_XZW 0x0D +#define TGSI_WRITEMASK_YZW 0x0E +#define TGSI_WRITEMASK_XYZW 0x0F + +#define TGSI_INTERPOLATE_CONSTANT 0 +#define TGSI_INTERPOLATE_LINEAR 1 +#define TGSI_INTERPOLATE_PERSPECTIVE 2 +#define TGSI_INTERPOLATE_COLOR 3 /* special color case for smooth/flat */ +#define TGSI_INTERPOLATE_COUNT 4 + +#define TGSI_INTERPOLATE_LOC_CENTER 0 +#define TGSI_INTERPOLATE_LOC_CENTROID 1 +#define TGSI_INTERPOLATE_LOC_SAMPLE 2 +#define TGSI_INTERPOLATE_LOC_COUNT 3 + +#define TGSI_CYLINDRICAL_WRAP_X (1 << 0) +#define TGSI_CYLINDRICAL_WRAP_Y (1 << 1) +#define TGSI_CYLINDRICAL_WRAP_Z (1 << 2) +#define TGSI_CYLINDRICAL_WRAP_W (1 << 3) + +enum tgsi_memory_type { + TGSI_MEMORY_TYPE_GLOBAL, /* OpenCL global */ + TGSI_MEMORY_TYPE_SHARED, /* OpenCL local / GLSL shared */ + TGSI_MEMORY_TYPE_PRIVATE, /* OpenCL private */ + TGSI_MEMORY_TYPE_INPUT, /* OpenCL kernel input params */ + TGSI_MEMORY_TYPE_COUNT, +}; + +struct tgsi_declaration +{ + unsigned Type : 4; /**< TGSI_TOKEN_TYPE_DECLARATION */ + unsigned NrTokens : 8; /**< UINT */ + unsigned File : 4; /**< one of TGSI_FILE_x */ + unsigned UsageMask : 4; /**< bitmask of TGSI_WRITEMASK_x flags */ + unsigned Dimension : 1; /**< any extra dimension info? */ + unsigned Semantic : 1; /**< BOOL, any semantic info? */ + unsigned Interpolate : 1; /**< any interpolation info? */ + unsigned Invariant : 1; /**< invariant optimization? */ + unsigned Local : 1; /**< optimize as subroutine local variable? */ + unsigned Array : 1; /**< extra array info? */ + unsigned Atomic : 1; /**< atomic only? for TGSI_FILE_BUFFER */ + unsigned MemType : 2; /**< TGSI_MEMORY_TYPE_x for TGSI_FILE_MEMORY */ + unsigned Padding : 3; +}; + +struct tgsi_declaration_range +{ + unsigned First : 16; /**< UINT */ + unsigned Last : 16; /**< UINT */ +}; + +struct tgsi_declaration_dimension +{ + unsigned Index2D:16; /**< UINT */ + unsigned Padding:16; +}; + +struct tgsi_declaration_interp +{ + unsigned Interpolate : 4; /**< one of TGSI_INTERPOLATE_x */ + unsigned Location : 2; /**< one of TGSI_INTERPOLATE_LOC_x */ + unsigned CylindricalWrap:4; /**< TGSI_CYLINDRICAL_WRAP_x flags */ + unsigned Padding : 22; +}; + +#define TGSI_SEMANTIC_POSITION 0 +#define TGSI_SEMANTIC_COLOR 1 +#define TGSI_SEMANTIC_BCOLOR 2 /**< back-face color */ +#define TGSI_SEMANTIC_FOG 3 +#define TGSI_SEMANTIC_PSIZE 4 +#define TGSI_SEMANTIC_GENERIC 5 +#define TGSI_SEMANTIC_NORMAL 6 +#define TGSI_SEMANTIC_FACE 7 +#define TGSI_SEMANTIC_EDGEFLAG 8 +#define TGSI_SEMANTIC_PRIMID 9 +#define TGSI_SEMANTIC_INSTANCEID 10 /**< doesn't include start_instance */ +#define TGSI_SEMANTIC_VERTEXID 11 +#define TGSI_SEMANTIC_STENCIL 12 +#define TGSI_SEMANTIC_CLIPDIST 13 +#define TGSI_SEMANTIC_CLIPVERTEX 14 +#define TGSI_SEMANTIC_GRID_SIZE 15 /**< grid size in blocks */ +#define TGSI_SEMANTIC_BLOCK_ID 16 /**< id of the current block */ +#define TGSI_SEMANTIC_BLOCK_SIZE 17 /**< block size in threads */ +#define TGSI_SEMANTIC_THREAD_ID 18 /**< block-relative id of the current thread */ +#define TGSI_SEMANTIC_TEXCOORD 19 /**< texture or sprite coordinates */ +#define TGSI_SEMANTIC_PCOORD 20 /**< point sprite coordinate */ +#define TGSI_SEMANTIC_VIEWPORT_INDEX 21 /**< viewport index */ +#define TGSI_SEMANTIC_LAYER 22 /**< layer (rendertarget index) */ +#define TGSI_SEMANTIC_CULLDIST 23 +#define TGSI_SEMANTIC_SAMPLEID 24 +#define TGSI_SEMANTIC_SAMPLEPOS 25 +#define TGSI_SEMANTIC_SAMPLEMASK 26 +#define TGSI_SEMANTIC_INVOCATIONID 27 +#define TGSI_SEMANTIC_VERTEXID_NOBASE 28 +#define TGSI_SEMANTIC_BASEVERTEX 29 +#define TGSI_SEMANTIC_PATCH 30 /**< generic per-patch semantic */ +#define TGSI_SEMANTIC_TESSCOORD 31 /**< coordinate being processed by tess */ +#define TGSI_SEMANTIC_TESSOUTER 32 /**< outer tessellation levels */ +#define TGSI_SEMANTIC_TESSINNER 33 /**< inner tessellation levels */ +#define TGSI_SEMANTIC_VERTICESIN 34 /**< number of input vertices */ +#define TGSI_SEMANTIC_HELPER_INVOCATION 35 /**< current invocation is helper */ +#define TGSI_SEMANTIC_COUNT 36 /**< number of semantic values */ + +struct tgsi_declaration_semantic +{ + unsigned Name : 8; /**< one of TGSI_SEMANTIC_x */ + unsigned Index : 16; /**< UINT */ + unsigned StreamX : 2; /**< vertex stream (for GS output) */ + unsigned StreamY : 2; + unsigned StreamZ : 2; + unsigned StreamW : 2; +}; + +struct tgsi_declaration_image { + unsigned Resource : 8; /**< one of TGSI_TEXTURE_ */ + unsigned Raw : 1; + unsigned Writable : 1; + unsigned Format : 10; /**< one of PIPE_FORMAT_ */ + unsigned Padding : 12; +}; + +enum tgsi_return_type { + TGSI_RETURN_TYPE_UNORM = 0, + TGSI_RETURN_TYPE_SNORM, + TGSI_RETURN_TYPE_SINT, + TGSI_RETURN_TYPE_UINT, + TGSI_RETURN_TYPE_FLOAT, + TGSI_RETURN_TYPE_COUNT +}; + +struct tgsi_declaration_sampler_view { + unsigned Resource : 8; /**< one of TGSI_TEXTURE_ */ + unsigned ReturnTypeX : 6; /**< one of enum tgsi_return_type */ + unsigned ReturnTypeY : 6; /**< one of enum tgsi_return_type */ + unsigned ReturnTypeZ : 6; /**< one of enum tgsi_return_type */ + unsigned ReturnTypeW : 6; /**< one of enum tgsi_return_type */ +}; + +struct tgsi_declaration_array { + unsigned ArrayID : 10; + unsigned Padding : 22; +}; + +/* + * Special resources that don't need to be declared. They map to the + * GLOBAL/LOCAL/PRIVATE/INPUT compute memory spaces. + */ +#define TGSI_RESOURCE_GLOBAL 0x7fff +#define TGSI_RESOURCE_LOCAL 0x7ffe +#define TGSI_RESOURCE_PRIVATE 0x7ffd +#define TGSI_RESOURCE_INPUT 0x7ffc + +#define TGSI_IMM_FLOAT32 0 +#define TGSI_IMM_UINT32 1 +#define TGSI_IMM_INT32 2 +#define TGSI_IMM_FLOAT64 3 + +struct tgsi_immediate +{ + unsigned Type : 4; /**< TGSI_TOKEN_TYPE_IMMEDIATE */ + unsigned NrTokens : 14; /**< UINT */ + unsigned DataType : 4; /**< one of TGSI_IMM_x */ + unsigned Padding : 10; +}; + +union tgsi_immediate_data +{ + float Float; + unsigned Uint; + int Int; +}; + +#define TGSI_PROPERTY_GS_INPUT_PRIM 0 +#define TGSI_PROPERTY_GS_OUTPUT_PRIM 1 +#define TGSI_PROPERTY_GS_MAX_OUTPUT_VERTICES 2 +#define TGSI_PROPERTY_FS_COORD_ORIGIN 3 +#define TGSI_PROPERTY_FS_COORD_PIXEL_CENTER 4 +#define TGSI_PROPERTY_FS_COLOR0_WRITES_ALL_CBUFS 5 +#define TGSI_PROPERTY_FS_DEPTH_LAYOUT 6 +#define TGSI_PROPERTY_VS_PROHIBIT_UCPS 7 +#define TGSI_PROPERTY_GS_INVOCATIONS 8 +#define TGSI_PROPERTY_VS_WINDOW_SPACE_POSITION 9 +#define TGSI_PROPERTY_TCS_VERTICES_OUT 10 +#define TGSI_PROPERTY_TES_PRIM_MODE 11 +#define TGSI_PROPERTY_TES_SPACING 12 +#define TGSI_PROPERTY_TES_VERTEX_ORDER_CW 13 +#define TGSI_PROPERTY_TES_POINT_MODE 14 +#define TGSI_PROPERTY_NUM_CLIPDIST_ENABLED 15 +#define TGSI_PROPERTY_NUM_CULLDIST_ENABLED 16 +#define TGSI_PROPERTY_FS_EARLY_DEPTH_STENCIL 17 +#define TGSI_PROPERTY_FS_POST_DEPTH_COVERAGE 18 +#define TGSI_PROPERTY_NEXT_SHADER 19 +#define TGSI_PROPERTY_CS_FIXED_BLOCK_WIDTH 20 +#define TGSI_PROPERTY_CS_FIXED_BLOCK_HEIGHT 21 +#define TGSI_PROPERTY_CS_FIXED_BLOCK_DEPTH 22 +#define TGSI_PROPERTY_MUL_ZERO_WINS 23 +#define TGSI_PROPERTY_COUNT 24 + +struct tgsi_property { + unsigned Type : 4; /**< TGSI_TOKEN_TYPE_PROPERTY */ + unsigned NrTokens : 8; /**< UINT */ + unsigned PropertyName : 8; /**< one of TGSI_PROPERTY */ + unsigned Padding : 12; +}; + +#define TGSI_FS_COORD_ORIGIN_UPPER_LEFT 0 +#define TGSI_FS_COORD_ORIGIN_LOWER_LEFT 1 + +#define TGSI_FS_COORD_PIXEL_CENTER_HALF_INTEGER 0 +#define TGSI_FS_COORD_PIXEL_CENTER_INTEGER 1 + +#define TGSI_FS_DEPTH_LAYOUT_NONE 0 +#define TGSI_FS_DEPTH_LAYOUT_ANY 1 +#define TGSI_FS_DEPTH_LAYOUT_GREATER 2 +#define TGSI_FS_DEPTH_LAYOUT_LESS 3 +#define TGSI_FS_DEPTH_LAYOUT_UNCHANGED 4 + + +struct tgsi_property_data { + unsigned Data; +}; + +/* TGSI opcodes. + * + * For more information on semantics of opcodes and + * which APIs are known to use which opcodes, see + * gallium/docs/source/tgsi.rst + */ +/* VIRGLRENDERER specific - DON'T SYNC WITH MESA + * OR REMOVE OPCODES - FILL in and REWRITE tgsi_info + * accordingly. + */ +#define TGSI_OPCODE_ARL 0 +#define TGSI_OPCODE_MOV 1 +#define TGSI_OPCODE_LIT 2 +#define TGSI_OPCODE_RCP 3 +#define TGSI_OPCODE_RSQ 4 +#define TGSI_OPCODE_EXP 5 +#define TGSI_OPCODE_LOG 6 +#define TGSI_OPCODE_MUL 7 +#define TGSI_OPCODE_ADD 8 +#define TGSI_OPCODE_DP3 9 +#define TGSI_OPCODE_DP4 10 +#define TGSI_OPCODE_DST 11 +#define TGSI_OPCODE_MIN 12 +#define TGSI_OPCODE_MAX 13 +#define TGSI_OPCODE_SLT 14 +#define TGSI_OPCODE_SGE 15 +#define TGSI_OPCODE_MAD 16 +#define TGSI_OPCODE_SUB 17 +#define TGSI_OPCODE_LRP 18 +#define TGSI_OPCODE_FMA 19 +#define TGSI_OPCODE_SQRT 20 + /* gap */ +#define TGSI_OPCODE_FRC 24 + /* gap */ +#define TGSI_OPCODE_FLR 26 +#define TGSI_OPCODE_ROUND 27 +#define TGSI_OPCODE_EX2 28 +#define TGSI_OPCODE_LG2 29 +#define TGSI_OPCODE_POW 30 +#define TGSI_OPCODE_XPD 31 + /* gap */ +#define TGSI_OPCODE_ABS 33 + /* gap */ +#define TGSI_OPCODE_DPH 35 +#define TGSI_OPCODE_COS 36 +#define TGSI_OPCODE_DDX 37 +#define TGSI_OPCODE_DDY 38 +#define TGSI_OPCODE_KILL 39 /* unconditional */ +#define TGSI_OPCODE_PK2H 40 +#define TGSI_OPCODE_PK2US 41 +#define TGSI_OPCODE_PK4B 42 +#define TGSI_OPCODE_PK4UB 43 + /* gap */ +#define TGSI_OPCODE_SEQ 45 + /* gap */ +#define TGSI_OPCODE_SGT 47 +#define TGSI_OPCODE_SIN 48 +#define TGSI_OPCODE_SLE 49 +#define TGSI_OPCODE_SNE 50 + /* gap */ +#define TGSI_OPCODE_TEX 52 +#define TGSI_OPCODE_TXD 53 +#define TGSI_OPCODE_TXP 54 +#define TGSI_OPCODE_UP2H 55 +#define TGSI_OPCODE_UP2US 56 +#define TGSI_OPCODE_UP4B 57 +#define TGSI_OPCODE_UP4UB 58 + /* gap */ +#define TGSI_OPCODE_ARR 61 + /* gap */ +#define TGSI_OPCODE_CAL 63 +#define TGSI_OPCODE_RET 64 +#define TGSI_OPCODE_SSG 65 /* SGN */ +#define TGSI_OPCODE_CMP 66 +#define TGSI_OPCODE_SCS 67 +#define TGSI_OPCODE_TXB 68 +#define TGSI_OPCODE_FBFETCH 69 +#define TGSI_OPCODE_DIV 70 +#define TGSI_OPCODE_DP2 71 +#define TGSI_OPCODE_TXL 72 +#define TGSI_OPCODE_BRK 73 +#define TGSI_OPCODE_IF 74 +#define TGSI_OPCODE_UIF 75 +#define TGSI_OPCODE_ELSE 77 +#define TGSI_OPCODE_ENDIF 78 + +#define TGSI_OPCODE_DDX_FINE 79 +#define TGSI_OPCODE_DDY_FINE 80 + /* gap */ +#define TGSI_OPCODE_CEIL 83 +#define TGSI_OPCODE_I2F 84 +#define TGSI_OPCODE_NOT 85 +#define TGSI_OPCODE_TRUNC 86 +#define TGSI_OPCODE_SHL 87 + /* gap */ +#define TGSI_OPCODE_AND 89 +#define TGSI_OPCODE_OR 90 +#define TGSI_OPCODE_MOD 91 +#define TGSI_OPCODE_XOR 92 + /* gap */ +#define TGSI_OPCODE_TXF 94 +#define TGSI_OPCODE_TXQ 95 +#define TGSI_OPCODE_CONT 96 +#define TGSI_OPCODE_EMIT 97 +#define TGSI_OPCODE_ENDPRIM 98 +#define TGSI_OPCODE_BGNLOOP 99 +#define TGSI_OPCODE_BGNSUB 100 +#define TGSI_OPCODE_ENDLOOP 101 +#define TGSI_OPCODE_ENDSUB 102 + /* gap */ +#define TGSI_OPCODE_TXQS 104 +#define TGSI_OPCODE_RESQ 105 + /* gap */ +#define TGSI_OPCODE_NOP 107 + +#define TGSI_OPCODE_FSEQ 108 +#define TGSI_OPCODE_FSGE 109 +#define TGSI_OPCODE_FSLT 110 +#define TGSI_OPCODE_FSNE 111 + +#define TGSI_OPCODE_MEMBAR 112 + /* gap */ +#define TGSI_OPCODE_KILL_IF 116 /* conditional kill */ +#define TGSI_OPCODE_END 117 /* aka HALT */ +#define TGSI_OPCODE_DFMA 118 +#define TGSI_OPCODE_F2I 119 +#define TGSI_OPCODE_IDIV 120 +#define TGSI_OPCODE_IMAX 121 +#define TGSI_OPCODE_IMIN 122 +#define TGSI_OPCODE_INEG 123 +#define TGSI_OPCODE_ISGE 124 +#define TGSI_OPCODE_ISHR 125 +#define TGSI_OPCODE_ISLT 126 +#define TGSI_OPCODE_F2U 127 +#define TGSI_OPCODE_U2F 128 +#define TGSI_OPCODE_UADD 129 +#define TGSI_OPCODE_UDIV 130 +#define TGSI_OPCODE_UMAD 131 +#define TGSI_OPCODE_UMAX 132 +#define TGSI_OPCODE_UMIN 133 +#define TGSI_OPCODE_UMOD 134 +#define TGSI_OPCODE_UMUL 135 +#define TGSI_OPCODE_USEQ 136 +#define TGSI_OPCODE_USGE 137 +#define TGSI_OPCODE_USHR 138 +#define TGSI_OPCODE_USLT 139 +#define TGSI_OPCODE_USNE 140 +#define TGSI_OPCODE_SWITCH 141 +#define TGSI_OPCODE_CASE 142 +#define TGSI_OPCODE_DEFAULT 143 +#define TGSI_OPCODE_ENDSWITCH 144 + +/* resource related opcodes */ +#define TGSI_OPCODE_SAMPLE 145 +#define TGSI_OPCODE_SAMPLE_I 146 +#define TGSI_OPCODE_SAMPLE_I_MS 147 +#define TGSI_OPCODE_SAMPLE_B 148 +#define TGSI_OPCODE_SAMPLE_C 149 +#define TGSI_OPCODE_SAMPLE_C_LZ 150 +#define TGSI_OPCODE_SAMPLE_D 151 +#define TGSI_OPCODE_SAMPLE_L 152 +#define TGSI_OPCODE_GATHER4 153 +#define TGSI_OPCODE_SVIEWINFO 154 +#define TGSI_OPCODE_SAMPLE_POS 155 +#define TGSI_OPCODE_SAMPLE_INFO 156 + +#define TGSI_OPCODE_UARL 157 +#define TGSI_OPCODE_UCMP 158 +#define TGSI_OPCODE_IABS 159 +#define TGSI_OPCODE_ISSG 160 + +#define TGSI_OPCODE_LOAD 161 +#define TGSI_OPCODE_STORE 162 + + /* gap */ +#define TGSI_OPCODE_BARRIER 166 + +#define TGSI_OPCODE_ATOMUADD 167 +#define TGSI_OPCODE_ATOMXCHG 168 +#define TGSI_OPCODE_ATOMCAS 169 +#define TGSI_OPCODE_ATOMAND 170 +#define TGSI_OPCODE_ATOMOR 171 +#define TGSI_OPCODE_ATOMXOR 172 +#define TGSI_OPCODE_ATOMUMIN 173 +#define TGSI_OPCODE_ATOMUMAX 174 +#define TGSI_OPCODE_ATOMIMIN 175 +#define TGSI_OPCODE_ATOMIMAX 176 + +/* to be used for shadow cube map compares */ +#define TGSI_OPCODE_TEX2 177 +#define TGSI_OPCODE_TXB2 178 +#define TGSI_OPCODE_TXL2 179 + +#define TGSI_OPCODE_IMUL_HI 180 +#define TGSI_OPCODE_UMUL_HI 181 + +#define TGSI_OPCODE_TG4 182 + +#define TGSI_OPCODE_LODQ 183 + +#define TGSI_OPCODE_IBFE 184 +#define TGSI_OPCODE_UBFE 185 +#define TGSI_OPCODE_BFI 186 +#define TGSI_OPCODE_BREV 187 +#define TGSI_OPCODE_POPC 188 +#define TGSI_OPCODE_LSB 189 +#define TGSI_OPCODE_IMSB 190 +#define TGSI_OPCODE_UMSB 191 + +#define TGSI_OPCODE_INTERP_CENTROID 192 +#define TGSI_OPCODE_INTERP_SAMPLE 193 +#define TGSI_OPCODE_INTERP_OFFSET 194 + +/* sm5 marked opcodes are supported in D3D11 optionally - also DMOV, DMOVC */ +#define TGSI_OPCODE_F2D 195 /* SM5 */ +#define TGSI_OPCODE_D2F 196 +#define TGSI_OPCODE_DABS 197 +#define TGSI_OPCODE_DNEG 198 /* SM5 */ +#define TGSI_OPCODE_DADD 199 /* SM5 */ +#define TGSI_OPCODE_DMUL 200 /* SM5 */ +#define TGSI_OPCODE_DMAX 201 /* SM5 */ +#define TGSI_OPCODE_DMIN 202 /* SM5 */ +#define TGSI_OPCODE_DSLT 203 /* SM5 */ +#define TGSI_OPCODE_DSGE 204 /* SM5 */ +#define TGSI_OPCODE_DSEQ 205 /* SM5 */ +#define TGSI_OPCODE_DSNE 206 /* SM5 */ +#define TGSI_OPCODE_DRCP 207 /* eg, cayman */ +#define TGSI_OPCODE_DSQRT 208 /* eg, cayman also has DRSQ */ +#define TGSI_OPCODE_DMAD 209 +#define TGSI_OPCODE_DFRAC 210 /* eg, cayman */ +#define TGSI_OPCODE_DLDEXP 211 /* eg, cayman */ +#define TGSI_OPCODE_DFRACEXP 212 /* eg, cayman */ +#define TGSI_OPCODE_D2I 213 +#define TGSI_OPCODE_I2D 214 +#define TGSI_OPCODE_D2U 215 +#define TGSI_OPCODE_U2D 216 +#define TGSI_OPCODE_DRSQ 217 /* eg, cayman also has DRSQ */ +#define TGSI_OPCODE_DTRUNC 218 /* nvc0 */ +#define TGSI_OPCODE_DCEIL 219 /* nvc0 */ +#define TGSI_OPCODE_DFLR 220 /* nvc0 */ +#define TGSI_OPCODE_DROUND 221 /* nvc0 */ +#define TGSI_OPCODE_DSSG 222 +#define TGSI_OPCODE_DDIV 223 +#define TGSI_OPCODE_CLOCK 224 + +/* opcodes for ARB_gpu_shader_int64 */ +#define TGSI_OPCODE_I64ABS 225 +#define TGSI_OPCODE_I64NEG 226 +#define TGSI_OPCODE_I64SSG 227 +#define TGSI_OPCODE_I64SLT 228 +#define TGSI_OPCODE_I64SGE 229 +#define TGSI_OPCODE_I64MIN 230 +#define TGSI_OPCODE_I64MAX 231 +#define TGSI_OPCODE_I64SHR 232 +#define TGSI_OPCODE_I64DIV 233 +#define TGSI_OPCODE_I64MOD 234 +#define TGSI_OPCODE_F2I64 235 +#define TGSI_OPCODE_U2I64 236 +#define TGSI_OPCODE_I2I64 237 +#define TGSI_OPCODE_D2I64 238 +#define TGSI_OPCODE_I642F 239 +#define TGSI_OPCODE_I642D 240 + +#define TGSI_OPCODE_U64ADD 241 +#define TGSI_OPCODE_U64MUL 242 +#define TGSI_OPCODE_U64SEQ 243 +#define TGSI_OPCODE_U64SNE 244 +#define TGSI_OPCODE_U64SLT 245 +#define TGSI_OPCODE_U64SGE 246 +#define TGSI_OPCODE_U64MIN 247 +#define TGSI_OPCODE_U64MAX 248 +#define TGSI_OPCODE_U64SHL 249 +#define TGSI_OPCODE_U64SHR 250 +#define TGSI_OPCODE_U64DIV 251 +#define TGSI_OPCODE_U64MOD 252 +#define TGSI_OPCODE_F2U64 253 +#define TGSI_OPCODE_D2U64 254 +#define TGSI_OPCODE_U642F 255 +#define TGSI_OPCODE_U642D 256 + +#define TGSI_OPCODE_LAST 257 + +/** + * Opcode is the operation code to execute. A given operation defines the + * semantics how the source registers (if any) are interpreted and what is + * written to the destination registers (if any) as a result of execution. + * + * NumDstRegs and NumSrcRegs is the number of destination and source registers, + * respectively. For a given operation code, those numbers are fixed and are + * present here only for convenience. + * + * Saturate controls how are final results in destination registers modified. + */ + +/* + * VIRGLRENDERER specific - + * we no long keep this in sync with mesa, we had to increase the NrTokens + * as mesa can remove old opcodes, but the renderer cannot. + */ +struct tgsi_instruction +{ + unsigned Type : 4; /* TGSI_TOKEN_TYPE_INSTRUCTION */ + unsigned NrTokens : 9; /* UINT */ + unsigned Opcode : 8; /* TGSI_OPCODE_ */ + unsigned Saturate : 1; /* BOOL */ + unsigned NumDstRegs : 2; /* UINT */ + unsigned NumSrcRegs : 4; /* UINT */ + unsigned Label : 1; + unsigned Texture : 1; + unsigned Memory : 1; + unsigned Precise : 1; +}; + +/* + * If tgsi_instruction::Label is TRUE, tgsi_instruction_label follows. + * + * If tgsi_instruction::Texture is TRUE, tgsi_instruction_texture follows. + * if texture instruction has a number of offsets, + * then tgsi_instruction::Texture::NumOffset of tgsi_texture_offset follow. + * + * Then, tgsi_instruction::NumDstRegs of tgsi_dst_register follow. + * + * Then, tgsi_instruction::NumSrcRegs of tgsi_src_register follow. + * + * tgsi_instruction::NrTokens contains the total number of words that make the + * instruction, including the instruction word. + */ + +#define TGSI_SWIZZLE_X 0 +#define TGSI_SWIZZLE_Y 1 +#define TGSI_SWIZZLE_Z 2 +#define TGSI_SWIZZLE_W 3 + +struct tgsi_instruction_label +{ + unsigned Label : 24; /* UINT */ + unsigned Padding : 8; +}; + +#define TGSI_TEXTURE_BUFFER 0 +#define TGSI_TEXTURE_1D 1 +#define TGSI_TEXTURE_2D 2 +#define TGSI_TEXTURE_3D 3 +#define TGSI_TEXTURE_CUBE 4 +#define TGSI_TEXTURE_RECT 5 +#define TGSI_TEXTURE_SHADOW1D 6 +#define TGSI_TEXTURE_SHADOW2D 7 +#define TGSI_TEXTURE_SHADOWRECT 8 +#define TGSI_TEXTURE_1D_ARRAY 9 +#define TGSI_TEXTURE_2D_ARRAY 10 +#define TGSI_TEXTURE_SHADOW1D_ARRAY 11 +#define TGSI_TEXTURE_SHADOW2D_ARRAY 12 +#define TGSI_TEXTURE_SHADOWCUBE 13 +#define TGSI_TEXTURE_2D_MSAA 14 +#define TGSI_TEXTURE_2D_ARRAY_MSAA 15 +#define TGSI_TEXTURE_CUBE_ARRAY 16 +#define TGSI_TEXTURE_SHADOWCUBE_ARRAY 17 +#define TGSI_TEXTURE_UNKNOWN 18 +#define TGSI_TEXTURE_COUNT 19 + +struct tgsi_instruction_texture +{ + unsigned Texture : 8; /* TGSI_TEXTURE_ */ + unsigned NumOffsets : 4; + unsigned Padding : 20; +}; + +/* for texture offsets in GLSL and DirectX. + * Generally these always come from TGSI_FILE_IMMEDIATE, + * however DX11 appears to have the capability to do + * non-constant texture offsets. + */ +struct tgsi_texture_offset +{ + int Index : 16; + unsigned File : 4; /**< one of TGSI_FILE_x */ + unsigned SwizzleX : 2; /* TGSI_SWIZZLE_x */ + unsigned SwizzleY : 2; /* TGSI_SWIZZLE_x */ + unsigned SwizzleZ : 2; /* TGSI_SWIZZLE_x */ + unsigned Padding : 6; +}; + +/** + * File specifies the register array to access. + * + * Index specifies the element number of a register in the register file. + * + * If Indirect is TRUE, Index should be offset by the X component of the indirect + * register that follows. The register can be now fetched into local storage + * for further processing. + * + * If Negate is TRUE, all components of the fetched register are negated. + * + * The fetched register components are swizzled according to SwizzleX, SwizzleY, + * SwizzleZ and SwizzleW. + * + */ + +struct tgsi_src_register +{ + unsigned File : 4; /* TGSI_FILE_ */ + unsigned Indirect : 1; /* BOOL */ + unsigned Dimension : 1; /* BOOL */ + int Index : 16; /* SINT */ + unsigned SwizzleX : 2; /* TGSI_SWIZZLE_ */ + unsigned SwizzleY : 2; /* TGSI_SWIZZLE_ */ + unsigned SwizzleZ : 2; /* TGSI_SWIZZLE_ */ + unsigned SwizzleW : 2; /* TGSI_SWIZZLE_ */ + unsigned Absolute : 1; /* BOOL */ + unsigned Negate : 1; /* BOOL */ +}; + +/** + * If tgsi_src_register::Indirect is TRUE, tgsi_ind_register follows. + * + * File, Index and Swizzle are handled the same as in tgsi_src_register. + * + * If ArrayID is zero the whole register file might be indirectly addressed, + * if not only the Declaration with this ArrayID is accessed by this operand. + * + */ + +struct tgsi_ind_register +{ + unsigned File : 4; /* TGSI_FILE_ */ + int Index : 16; /* SINT */ + unsigned Swizzle : 2; /* TGSI_SWIZZLE_ */ + unsigned ArrayID : 10; /* UINT */ +}; + +/** + * If tgsi_src_register::Dimension is TRUE, tgsi_dimension follows. + */ + +struct tgsi_dimension +{ + unsigned Indirect : 1; /* BOOL */ + unsigned Dimension : 1; /* BOOL */ + unsigned Padding : 14; + int Index : 16; /* SINT */ +}; + +struct tgsi_dst_register +{ + unsigned File : 4; /* TGSI_FILE_ */ + unsigned WriteMask : 4; /* TGSI_WRITEMASK_ */ + unsigned Indirect : 1; /* BOOL */ + unsigned Dimension : 1; /* BOOL */ + int Index : 16; /* SINT */ + unsigned Padding : 6; +}; + +#define TGSI_MEMORY_COHERENT (1 << 0) +#define TGSI_MEMORY_RESTRICT (1 << 1) +#define TGSI_MEMORY_VOLATILE (1 << 2) + +/** + * Specifies the type of memory access to do for the LOAD/STORE instruction. + */ +struct tgsi_instruction_memory +{ + unsigned Qualifier : 3; /* TGSI_MEMORY_ */ + unsigned Texture : 8; /* only for images: TGSI_TEXTURE_ */ + unsigned Format : 10; /* only for images: PIPE_FORMAT_ */ + unsigned Padding : 11; +}; + +#define TGSI_MEMBAR_SHADER_BUFFER (1 << 0) +#define TGSI_MEMBAR_ATOMIC_BUFFER (1 << 1) +#define TGSI_MEMBAR_SHADER_IMAGE (1 << 2) +#define TGSI_MEMBAR_SHARED (1 << 3) +#define TGSI_MEMBAR_THREAD_GROUP (1 << 4) + +#ifdef __cplusplus +} +#endif + +#endif /* P_SHADER_TOKENS_H */ diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_state.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_state.h new file mode 100644 index 0000000..f0a7b55 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_state.h @@ -0,0 +1,660 @@ +/************************************************************************** + * + * Copyright 2007 VMware, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + + +/** + * @file + * + * Abstract graphics pipe state objects. + * + * Basic notes: + * 1. Want compact representations, so we use bitfields. + * 2. Put bitfields before other (GLfloat) fields. + */ + + +#ifndef PIPE_STATE_H +#define PIPE_STATE_H + +#include "p_compiler.h" +#include "p_defines.h" +#include "p_format.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Implementation limits + */ +#define PIPE_MAX_ATTRIBS 32 +#define PIPE_MAX_CLIP_PLANES 8 +#define PIPE_MAX_COLOR_BUFS 8 +#define PIPE_MAX_CONSTANT_BUFFERS 32 +#define PIPE_MAX_SAMPLERS 16 +#define PIPE_MAX_SHADER_INPUTS 80 /* 32 GENERIC + 32 PATCH + 16 others */ +#define PIPE_MAX_SHADER_OUTPUTS 80 /* 32 GENERIC + 32 PATCH + 16 others */ +#define PIPE_MAX_SHADER_SAMPLER_VIEWS 32 +#define PIPE_MAX_SHADER_BUFFERS 32 +#define PIPE_MAX_SHADER_IMAGES 32 +#define PIPE_MAX_TEXTURE_LEVELS 16 +#define PIPE_MAX_SO_BUFFERS 4 +#define PIPE_MAX_SO_OUTPUTS 64 +#define PIPE_MAX_VIEWPORTS 16 +#define PIPE_MAX_CLIP_OR_CULL_DISTANCE_COUNT 8 +#define PIPE_MAX_CLIP_OR_CULL_DISTANCE_ELEMENT_COUNT 2 +#define PIPE_MAX_HW_ATOMIC_BUFFERS 32 + +struct pipe_reference +{ + int32_t count; /* atomic */ +}; + + + +/** + * Primitive (point/line/tri) rasterization info + */ +struct pipe_rasterizer_state +{ + unsigned flatshade:1; + unsigned light_twoside:1; + unsigned clamp_vertex_color:1; + unsigned clamp_fragment_color:1; + unsigned front_ccw:1; + unsigned cull_face:2; /**< PIPE_FACE_x */ + unsigned fill_front:2; /**< PIPE_POLYGON_MODE_x */ + unsigned fill_back:2; /**< PIPE_POLYGON_MODE_x */ + unsigned offset_point:1; + unsigned offset_line:1; + unsigned offset_tri:1; + unsigned scissor:1; + unsigned poly_smooth:1; + unsigned poly_stipple_enable:1; + unsigned point_smooth:1; + unsigned sprite_coord_mode:1; /**< PIPE_SPRITE_COORD_ */ + unsigned point_quad_rasterization:1; /** points rasterized as quads or points */ + unsigned point_tri_clip:1; /** large points clipped as tris or points */ + unsigned point_size_per_vertex:1; /**< size computed in vertex shader */ + unsigned multisample:1; /* XXX maybe more ms state in future */ + unsigned force_persample_interp:1; + unsigned line_smooth:1; + unsigned line_stipple_enable:1; + unsigned line_last_pixel:1; + + /** + * Use the first vertex of a primitive as the provoking vertex for + * flat shading. + */ + unsigned flatshade_first:1; + + unsigned half_pixel_center:1; + unsigned bottom_edge_rule:1; + + /** + * When true, rasterization is disabled and no pixels are written. + * This only makes sense with the Stream Out functionality. + */ + unsigned rasterizer_discard:1; + + /** + * When false, depth clipping is disabled and the depth value will be + * clamped later at the per-pixel level before depth testing. + * This depends on PIPE_CAP_DEPTH_CLIP_DISABLE. + */ + unsigned depth_clip:1; + + /** + * When true clip space in the z axis goes from [0..1] (D3D). When false + * [-1, 1] (GL). + * + * NOTE: D3D will always use depth clamping. + */ + unsigned clip_halfz:1; + + /** + * Enable bits for clipping half-spaces. + * This applies to both user clip planes and shader clip distances. + * Note that if the bound shader exports any clip distances, these + * replace all user clip planes, and clip half-spaces enabled here + * but not written by the shader count as disabled. + */ + unsigned clip_plane_enable:PIPE_MAX_CLIP_PLANES; + + unsigned line_stipple_factor:8; /**< [1..256] actually */ + unsigned line_stipple_pattern:16; + + uint32_t sprite_coord_enable; /* referring to 32 TEXCOORD/GENERIC inputs */ + + float line_width; + float point_size; /**< used when no per-vertex size */ + float offset_units; + float offset_scale; + float offset_clamp; +}; + + +struct pipe_poly_stipple +{ + unsigned stipple[32]; +}; + + +struct pipe_viewport_state +{ + float scale[3]; + float translate[3]; +}; + + +struct pipe_scissor_state +{ + unsigned minx:16; + unsigned miny:16; + unsigned maxx:16; + unsigned maxy:16; +}; + + +struct pipe_clip_state +{ + float ucp[PIPE_MAX_CLIP_PLANES][4]; +}; + + +/** + * Stream output for vertex transform feedback. + */ +struct pipe_stream_output_info +{ + unsigned num_outputs; + /** stride for an entire vertex for each buffer in dwords */ + unsigned stride[PIPE_MAX_SO_BUFFERS]; + + /** + * Array of stream outputs, in the order they are to be written in. + * Selected components are tightly packed into the output buffer. + */ + struct { + unsigned register_index:8; /**< 0 to PIPE_MAX_SHADER_OUTPUTS */ + unsigned start_component:2; /** 0 to 3 */ + unsigned num_components:3; /** 1 to 4 */ + unsigned output_buffer:3; /**< 0 to PIPE_MAX_SO_BUFFERS */ + unsigned dst_offset:16; /**< offset into the buffer in dwords */ + unsigned stream:2; + unsigned need_temp:1; + } output[PIPE_MAX_SO_OUTPUTS]; +}; + + +struct pipe_shader_state +{ + const struct tgsi_token *tokens; + struct pipe_stream_output_info stream_output; +}; + + +struct pipe_depth_state +{ + unsigned enabled:1; /**< depth test enabled? */ + unsigned writemask:1; /**< allow depth buffer writes? */ + unsigned func:3; /**< depth test func (PIPE_FUNC_x) */ +}; + + +struct pipe_stencil_state +{ + unsigned enabled:1; /**< stencil[0]: stencil enabled, stencil[1]: two-side enabled */ + unsigned func:3; /**< PIPE_FUNC_x */ + unsigned fail_op:3; /**< PIPE_STENCIL_OP_x */ + unsigned zpass_op:3; /**< PIPE_STENCIL_OP_x */ + unsigned zfail_op:3; /**< PIPE_STENCIL_OP_x */ + unsigned valuemask:8; + unsigned writemask:8; +}; + + +struct pipe_alpha_state +{ + unsigned enabled:1; + unsigned func:3; /**< PIPE_FUNC_x */ + float ref_value; /**< reference value */ +}; + + +struct pipe_depth_stencil_alpha_state +{ + struct pipe_depth_state depth; + struct pipe_stencil_state stencil[2]; /**< [0] = front, [1] = back */ + struct pipe_alpha_state alpha; +}; + + +struct pipe_rt_blend_state +{ + unsigned blend_enable:1; + + unsigned rgb_func:3; /**< PIPE_BLEND_x */ + unsigned rgb_src_factor:5; /**< PIPE_BLENDFACTOR_x */ + unsigned rgb_dst_factor:5; /**< PIPE_BLENDFACTOR_x */ + + unsigned alpha_func:3; /**< PIPE_BLEND_x */ + unsigned alpha_src_factor:5; /**< PIPE_BLENDFACTOR_x */ + unsigned alpha_dst_factor:5; /**< PIPE_BLENDFACTOR_x */ + + unsigned colormask:4; /**< bitmask of PIPE_MASK_R/G/B/A */ +}; + +struct pipe_blend_state +{ + unsigned independent_blend_enable:1; + unsigned logicop_enable:1; + unsigned logicop_func:4; /**< PIPE_LOGICOP_x */ + unsigned dither:1; + unsigned alpha_to_coverage:1; + unsigned alpha_to_one:1; + struct pipe_rt_blend_state rt[PIPE_MAX_COLOR_BUFS]; +}; + + +struct pipe_blend_color +{ + float color[4]; +}; + +struct pipe_stencil_ref +{ + ubyte ref_value[2]; +}; + +struct pipe_framebuffer_state +{ + unsigned width, height; + + /** multiple color buffers for multiple render targets */ + unsigned nr_cbufs; + struct pipe_surface *cbufs[PIPE_MAX_COLOR_BUFS]; + + struct pipe_surface *zsbuf; /**< Z/stencil buffer */ +}; + + +/** + * Texture sampler state. + */ +struct pipe_sampler_state +{ + unsigned wrap_s:3; /**< PIPE_TEX_WRAP_x */ + unsigned wrap_t:3; /**< PIPE_TEX_WRAP_x */ + unsigned wrap_r:3; /**< PIPE_TEX_WRAP_x */ + unsigned min_img_filter:2; /**< PIPE_TEX_FILTER_x */ + unsigned min_mip_filter:2; /**< PIPE_TEX_MIPFILTER_x */ + unsigned mag_img_filter:2; /**< PIPE_TEX_FILTER_x */ + unsigned compare_mode:1; /**< PIPE_TEX_COMPARE_x */ + unsigned compare_func:3; /**< PIPE_FUNC_x */ + unsigned normalized_coords:1; /**< Are coords normalized to [0,1]? */ + unsigned max_anisotropy:6; + unsigned seamless_cube_map:1; + float lod_bias; /**< LOD/lambda bias */ + float min_lod, max_lod; /**< LOD clamp range, after bias */ + union pipe_color_union border_color; +}; + + +/** + * A view into a texture that can be bound to a color render target / + * depth stencil attachment point. + */ +struct pipe_surface +{ + struct pipe_reference reference; + struct pipe_resource *texture; /**< resource into which this is a view */ + struct pipe_context *context; /**< context this surface belongs to */ + enum pipe_format format; + + /* XXX width/height should be removed */ + unsigned width; /**< logical width in pixels */ + unsigned height; /**< logical height in pixels */ + + unsigned writable:1; /**< writable shader resource */ + + union { + struct { + unsigned level; + unsigned first_layer:16; + unsigned last_layer:16; + } tex; + struct { + unsigned first_element; + unsigned last_element; + } buf; + } u; +}; + + +/** + * A view into a texture that can be bound to a shader stage. + */ +struct pipe_sampler_view +{ + struct pipe_reference reference; + enum pipe_format format; /**< typed PIPE_FORMAT_x */ + struct pipe_resource *texture; /**< texture into which this is a view */ + struct pipe_context *context; /**< context this view belongs to */ + union { + struct { + unsigned first_layer:16; /**< first layer to use for array textures */ + unsigned last_layer:16; /**< last layer to use for array textures */ + unsigned first_level:8; /**< first mipmap level to use */ + unsigned last_level:8; /**< last mipmap level to use */ + } tex; + struct { + unsigned first_element; + unsigned last_element; + } buf; + } u; + unsigned swizzle_r:3; /**< PIPE_SWIZZLE_x for red component */ + unsigned swizzle_g:3; /**< PIPE_SWIZZLE_x for green component */ + unsigned swizzle_b:3; /**< PIPE_SWIZZLE_x for blue component */ + unsigned swizzle_a:3; /**< PIPE_SWIZZLE_x for alpha component */ +}; + + +/** + * Subregion of 1D/2D/3D image resource. + */ +struct pipe_box +{ + int x; + int y; + int z; + int width; + int height; + int depth; +}; + + +/** + * A memory object/resource such as a vertex buffer or texture. + */ +struct pipe_resource +{ + struct pipe_reference reference; + struct pipe_screen *screen; /**< screen that this texture belongs to */ + enum pipe_texture_target target; /**< PIPE_TEXTURE_x */ + enum pipe_format format; /**< PIPE_FORMAT_x */ + + unsigned width0; + unsigned height0; + unsigned depth0; + unsigned array_size; + + unsigned last_level:8; /**< Index of last mipmap level present/defined */ + unsigned nr_samples:8; /**< for multisampled surfaces, nr of samples */ + unsigned usage:8; /**< PIPE_USAGE_x (not a bitmask) */ + + unsigned bind; /**< bitmask of PIPE_BIND_x */ + unsigned flags; /**< bitmask of PIPE_RESOURCE_FLAG_x */ +}; + + +/** + * Transfer object. For data transfer to/from a resource. + */ +struct pipe_transfer +{ + struct pipe_resource *resource; /**< resource to transfer to/from */ + unsigned level; /**< texture mipmap level */ + enum pipe_transfer_usage usage; + struct pipe_box box; /**< region of the resource to access */ + unsigned stride; /**< row stride in bytes */ + unsigned layer_stride; /**< image/layer stride in bytes */ +}; + + + +/** + * A vertex buffer. Typically, all the vertex data/attributes for + * drawing something will be in one buffer. But it's also possible, for + * example, to put colors in one buffer and texcoords in another. + */ +struct pipe_vertex_buffer +{ + unsigned stride; /**< stride to same attrib in next vertex, in bytes */ + unsigned buffer_offset; /**< offset to start of data in buffer, in bytes */ + struct pipe_resource *buffer; /**< the actual buffer */ + const void *user_buffer; /**< pointer to a user buffer if buffer == NULL */ +}; + + +/** + * A constant buffer. A subrange of an existing buffer can be set + * as a constant buffer. + */ +struct pipe_constant_buffer { + struct pipe_resource *buffer; /**< the actual buffer */ + unsigned buffer_offset; /**< offset to start of data in buffer, in bytes */ + unsigned buffer_size; /**< how much data can be read in shader */ + const void *user_buffer; /**< pointer to a user buffer if buffer == NULL */ +}; + + +/** + * A stream output target. The structure specifies the range vertices can + * be written to. + * + * In addition to that, the structure should internally maintain the offset + * into the buffer, which should be incremented everytime something is written + * (appended) to it. The internal offset is buffer_offset + how many bytes + * have been written. The internal offset can be stored on the device + * and the CPU actually doesn't have to query it. + * + * Note that the buffer_size variable is actually specifying the available + * space in the buffer, not the size of the attached buffer. + * In other words in majority of cases buffer_size would simply be + * 'buffer->width0 - buffer_offset', so buffer_size refers to the size + * of the buffer left, after accounting for buffer offset, for stream output + * to write to. + * + * Use PIPE_QUERY_SO_STATISTICS to know how many primitives have + * actually been written. + */ +struct pipe_stream_output_target +{ + struct pipe_reference reference; + struct pipe_resource *buffer; /**< the output buffer */ + struct pipe_context *context; /**< context this SO target belongs to */ + + unsigned buffer_offset; /**< offset where data should be written, in bytes */ + unsigned buffer_size; /**< how much data is allowed to be written */ +}; + + +/** + * Information to describe a vertex attribute (position, color, etc) + */ +struct pipe_vertex_element +{ + /** Offset of this attribute, in bytes, from the start of the vertex */ + unsigned src_offset; + + /** Instance data rate divisor. 0 means this is per-vertex data, + * n means per-instance data used for n consecutive instances (n > 0). + */ + unsigned instance_divisor; + + /** Which vertex_buffer (as given to pipe->set_vertex_buffer()) does + * this attribute live in? + */ + unsigned vertex_buffer_index; + + enum pipe_format src_format; +}; + + +/** + * An index buffer. When an index buffer is bound, all indices to vertices + * will be looked up in the buffer. + */ +struct pipe_index_buffer +{ + unsigned index_size; /**< size of an index, in bytes */ + unsigned offset; /**< offset to start of data in buffer, in bytes */ + struct pipe_resource *buffer; /**< the actual buffer */ + const void *user_buffer; /**< pointer to a user buffer if buffer == NULL */ +}; + +struct pipe_draw_indirect_info +{ + unsigned offset; /**< must be 4 byte aligned */ + unsigned stride; /**< must be 4 byte aligned */ + unsigned draw_count; /**< number of indirect draws */ + unsigned indirect_draw_count_offset; /**< must be 4 byte aligned */ + + /* Indirect draw parameters resource is laid out as follows: + * + * if using indexed drawing: + * struct { + * uint32_t count; + * uint32_t instance_count; + * uint32_t start; + * int32_t index_bias; + * uint32_t start_instance; + * }; + * otherwise: + * struct { + * uint32_t count; + * uint32_t instance_count; + * uint32_t start; + * uint32_t start_instance; + * }; + */ + struct pipe_resource *buffer; + + /* Indirect draw count resource: If not NULL, contains a 32-bit value which + * is to be used as the real draw_count. + */ + struct pipe_resource *indirect_draw_count; +}; + +/** + * Information to describe a draw_vbo call. + */ +struct pipe_draw_info +{ + boolean indexed; /**< use index buffer */ + ubyte vertices_per_patch; /**< the number of vertices per patch */ + + unsigned mode; /**< the mode of the primitive */ + unsigned start; /**< the index of the first vertex */ + unsigned count; /**< number of vertices */ + + unsigned start_instance; /**< first instance id */ + unsigned instance_count; /**< number of instances */ + + unsigned drawid; /**< id of this draw in a multidraw */ + /** + * For indexed drawing, these fields apply after index lookup. + */ + int index_bias; /**< a bias to be added to each index */ + unsigned min_index; /**< the min index */ + unsigned max_index; /**< the max index */ + + /** + * Primitive restart enable/index (only applies to indexed drawing) + */ + boolean primitive_restart; + unsigned restart_index; + + struct pipe_draw_indirect_info indirect; + /** + * Stream output target. If not NULL, it's used to provide the 'count' + * parameter based on the number vertices captured by the stream output + * stage. (or generally, based on the number of bytes captured) + * + * Only 'mode', 'start_instance', and 'instance_count' are taken into + * account, all the other variables from pipe_draw_info are ignored. + * + * 'start' is implicitly 0 and 'count' is set as discussed above. + * The draw command is non-indexed. + * + * Note that this only provides the count. The vertex buffers must + * be set via set_vertex_buffers manually. + */ + struct pipe_stream_output_target *count_from_stream_output; +}; + + +/** + * Information to describe a blit call. + */ +struct pipe_blit_info +{ + struct { + struct pipe_resource *resource; + unsigned level; + struct pipe_box box; /**< negative width, height only legal for src */ + /* For pipe_surface-like format casting: */ + enum pipe_format format; /**< must be supported for sampling (src) + or rendering (dst), ZS is always supported */ + } dst, src; + + unsigned mask; /**< bitmask of PIPE_MASK_R/G/B/A/Z/S */ + unsigned filter; /**< PIPE_TEX_FILTER_* */ + + boolean scissor_enable; + struct pipe_scissor_state scissor; + + boolean render_condition_enable; /**< whether the blit should honor the + current render condition */ + boolean alpha_blend; /* dst.rgb = src.rgb * src.a + dst.rgb * (1 - src.a) */ +}; + + +/** + * Structure used as a header for serialized LLVM programs. + */ +struct pipe_llvm_program_header +{ + uint32_t num_bytes; /**< Number of bytes in the LLVM bytecode program. */ +}; + +struct pipe_compute_state +{ + const void *prog; /**< Compute program to be executed. */ + unsigned req_local_mem; /**< Required size of the LOCAL resource. */ + unsigned req_private_mem; /**< Required size of the PRIVATE resource. */ + unsigned req_input_mem; /**< Required size of the INPUT resource. */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_video_enums.h b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_video_enums.h new file mode 100644 index 0000000..10205ac --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/gallium/include/pipe/p_video_enums.h @@ -0,0 +1,79 @@ +/************************************************************************** + * + * Copyright 2009 Younes Manton. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef PIPE_VIDEO_ENUMS_H +#define PIPE_VIDEO_ENUMS_H + +enum pipe_video_format +{ + PIPE_VIDEO_FORMAT_UNKNOWN = 0, + PIPE_VIDEO_FORMAT_MPEG12, /**< MPEG1, MPEG2 */ + PIPE_VIDEO_FORMAT_MPEG4, /**< DIVX, XVID */ + PIPE_VIDEO_FORMAT_VC1, /**< WMV */ + PIPE_VIDEO_FORMAT_MPEG4_AVC /**< H.264 */ +}; + +enum pipe_video_profile +{ + PIPE_VIDEO_PROFILE_UNKNOWN, + PIPE_VIDEO_PROFILE_MPEG1, + PIPE_VIDEO_PROFILE_MPEG2_SIMPLE, + PIPE_VIDEO_PROFILE_MPEG2_MAIN, + PIPE_VIDEO_PROFILE_MPEG4_SIMPLE, + PIPE_VIDEO_PROFILE_MPEG4_ADVANCED_SIMPLE, + PIPE_VIDEO_PROFILE_VC1_SIMPLE, + PIPE_VIDEO_PROFILE_VC1_MAIN, + PIPE_VIDEO_PROFILE_VC1_ADVANCED, + PIPE_VIDEO_PROFILE_MPEG4_AVC_BASELINE, + PIPE_VIDEO_PROFILE_MPEG4_AVC_MAIN, + PIPE_VIDEO_PROFILE_MPEG4_AVC_HIGH +}; + +/* Video caps, can be different for each codec/profile */ +enum pipe_video_cap +{ + PIPE_VIDEO_CAP_SUPPORTED = 0, + PIPE_VIDEO_CAP_NPOT_TEXTURES = 1, + PIPE_VIDEO_CAP_MAX_WIDTH = 2, + PIPE_VIDEO_CAP_MAX_HEIGHT = 3, + PIPE_VIDEO_CAP_PREFERED_FORMAT = 4, + PIPE_VIDEO_CAP_PREFERS_INTERLACED = 5, + PIPE_VIDEO_CAP_SUPPORTS_PROGRESSIVE = 6, + PIPE_VIDEO_CAP_SUPPORTS_INTERLACED = 7, + PIPE_VIDEO_CAP_MAX_LEVEL = 8 +}; + +enum pipe_video_entrypoint +{ + PIPE_VIDEO_ENTRYPOINT_UNKNOWN, + PIPE_VIDEO_ENTRYPOINT_BITSTREAM, + PIPE_VIDEO_ENTRYPOINT_IDCT, + PIPE_VIDEO_ENTRYPOINT_MC, + PIPE_VIDEO_ENTRYPOINT_ENCODE +}; + +#endif /* PIPE_VIDEO_ENUMS_H */ diff --git a/app/src/main/cpp/virglrenderer/src/iov.c b/app/src/main/cpp/virglrenderer/src/iov.c new file mode 100644 index 0000000..0ee58b2 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/iov.c @@ -0,0 +1,204 @@ +/* + * this code is taken from Michael - the qemu code is GPLv2 so I don't want + * to reuse it. + * I've adapted it to handle offsets and callback + */ + +// +// iovec.c +// +// Scatter/gather utility routines +// +// Copyright (C) 2002 Michael Ringgaard. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// + +#include +#include +#include +#include +#include "vrend_iov.h" + +size_t vrend_get_iovec_size(const struct iovec *iov, int iovlen) { + size_t size = 0; + + while (iovlen > 0) { + size += iov->iov_len; + iov++; + iovlen--; + } + + return size; +} + +size_t vrend_read_from_iovec(const struct iovec *iov, int iovlen, + size_t offset, + char *buf, size_t count) +{ + size_t read = 0; + size_t len; + + while (count > 0 && iovlen > 0) { + if (iov->iov_len > offset) { + len = iov->iov_len - offset; + + if (count < len) len = count; + + memcpy(buf, (char*)iov->iov_base + offset, len); + read += len; + + buf += len; + count -= len; + offset = 0; + } else { + offset -= iov->iov_len; + } + + iov++; + iovlen--; + } + assert(offset == 0); + return read; +} + +size_t vrend_write_to_iovec(const struct iovec *iov, int iovlen, + size_t offset, const char *buf, size_t count) +{ + size_t written = 0; + size_t len; + + while (count > 0 && iovlen > 0) { + if (iov->iov_len > offset) { + len = iov->iov_len - offset; + + if (count < len) len = count; + + memcpy((char*)iov->iov_base + offset, buf, len); + written += len; + + offset = 0; + buf += len; + count -= len; + } else { + offset -= iov->iov_len; + } + iov++; + iovlen--; + } + assert(offset == 0); + return written; +} + +size_t vrend_read_from_iovec_cb(const struct iovec *iov, int iovlen, + size_t offset, size_t count, + iov_cb iocb, void *cookie) +{ + size_t read = 0; + size_t len; + + while (count > 0 && iovlen > 0) { + if (iov->iov_len > offset) { + len = iov->iov_len - offset; + + if (count < len) len = count; + + (*iocb)(cookie, read, (char*)iov->iov_base + offset, len); + read += len; + + count -= len; + offset = 0; + } else { + offset -= iov->iov_len; + } + iov++; + iovlen--; + } + assert(offset == 0); + return read; + + +} + +/** + * Copy data from one iovec to another iovec. + * + * TODO: Implement iovec copy without copy to intermediate buffer. + * + * \param src_iov The source iov. + * \param src_iovlen The number of memory regions in the source iov. + * \param src_offset The byte offset in the source iov to start reading from. + * \param dst_iov The destination iov. + * \param dst_iovlen The number of memory regions in the destination iov. + * \param dst_offset The byte offset in the destination iov to start writing to. + * \param count The number of bytes to copy + * \param buf If not NULL, a pointer to a buffer of at least count size + * to use a temporary storage for the copy operation. + * \return -1 on failure, 0 on success + */ +int vrend_copy_iovec(const struct iovec *src_iov, int src_iovlen, size_t src_offset, + const struct iovec *dst_iov, int dst_iovlen, size_t dst_offset, + size_t count, char *buf) +{ + int ret = 0; + bool needs_free; + size_t nread; + size_t nwritten; + + if (src_iov == NULL || dst_iov == NULL) + return -1; + + if (src_iov == dst_iov && src_offset == dst_offset) + return 0; + + if (!buf) { + buf = malloc(count); + needs_free = true; + } else { + needs_free = false; + } + + if (!buf) + return -1; + + nread = vrend_read_from_iovec(src_iov, src_iovlen, src_offset, buf, count); + if (nread != count) { + ret = -1; + goto out; + } + + nwritten = vrend_write_to_iovec(dst_iov, dst_iovlen, dst_offset, buf, count); + if (nwritten != count) { + ret = -1; + goto out; + } + +out: + if (needs_free) + free(buf); + + return ret; +} diff --git a/app/src/main/cpp/virglrenderer/src/virgl_hw.h b/app/src/main/cpp/virglrenderer/src/virgl_hw.h new file mode 100644 index 0000000..b32d5e0 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/virgl_hw.h @@ -0,0 +1,535 @@ +/* + * Copyright 2014, 2015 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VIRGL_HW_H +#define VIRGL_HW_H + +#include + +/* formats known by the HW device - based on gallium subset */ +enum virgl_formats { + VIRGL_FORMAT_NONE = 0, + VIRGL_FORMAT_B8G8R8A8_UNORM = 1, + VIRGL_FORMAT_B8G8R8X8_UNORM = 2, + VIRGL_FORMAT_A8R8G8B8_UNORM = 3, + VIRGL_FORMAT_X8R8G8B8_UNORM = 4, + VIRGL_FORMAT_B5G5R5A1_UNORM = 5, + VIRGL_FORMAT_B4G4R4A4_UNORM = 6, + VIRGL_FORMAT_B5G6R5_UNORM = 7, + VIRGL_FORMAT_R10G10B10A2_UNORM = 8, + VIRGL_FORMAT_L8_UNORM = 9, /**< ubyte luminance */ + VIRGL_FORMAT_A8_UNORM = 10, /**< ubyte alpha */ + VIRGL_FORMAT_I8_UNORM = 11, + VIRGL_FORMAT_L8A8_UNORM = 12, /**< ubyte alpha, luminance */ + VIRGL_FORMAT_L16_UNORM = 13, /**< ushort luminance */ + VIRGL_FORMAT_UYVY = 14, + VIRGL_FORMAT_YUYV = 15, + VIRGL_FORMAT_Z16_UNORM = 16, + VIRGL_FORMAT_Z32_UNORM = 17, + VIRGL_FORMAT_Z32_FLOAT = 18, + VIRGL_FORMAT_Z24_UNORM_S8_UINT = 19, + VIRGL_FORMAT_S8_UINT_Z24_UNORM = 20, + VIRGL_FORMAT_Z24X8_UNORM = 21, + VIRGL_FORMAT_X8Z24_UNORM = 22, + VIRGL_FORMAT_S8_UINT = 23, /**< ubyte stencil */ + VIRGL_FORMAT_R64_FLOAT = 24, + VIRGL_FORMAT_R64G64_FLOAT = 25, + VIRGL_FORMAT_R64G64B64_FLOAT = 26, + VIRGL_FORMAT_R64G64B64A64_FLOAT = 27, + VIRGL_FORMAT_R32_FLOAT = 28, + VIRGL_FORMAT_R32G32_FLOAT = 29, + VIRGL_FORMAT_R32G32B32_FLOAT = 30, + VIRGL_FORMAT_R32G32B32A32_FLOAT = 31, + + VIRGL_FORMAT_R32_UNORM = 32, + VIRGL_FORMAT_R32G32_UNORM = 33, + VIRGL_FORMAT_R32G32B32_UNORM = 34, + VIRGL_FORMAT_R32G32B32A32_UNORM = 35, + VIRGL_FORMAT_R32_USCALED = 36, + VIRGL_FORMAT_R32G32_USCALED = 37, + VIRGL_FORMAT_R32G32B32_USCALED = 38, + VIRGL_FORMAT_R32G32B32A32_USCALED = 39, + VIRGL_FORMAT_R32_SNORM = 40, + VIRGL_FORMAT_R32G32_SNORM = 41, + VIRGL_FORMAT_R32G32B32_SNORM = 42, + VIRGL_FORMAT_R32G32B32A32_SNORM = 43, + VIRGL_FORMAT_R32_SSCALED = 44, + VIRGL_FORMAT_R32G32_SSCALED = 45, + VIRGL_FORMAT_R32G32B32_SSCALED = 46, + VIRGL_FORMAT_R32G32B32A32_SSCALED = 47, + + VIRGL_FORMAT_R16_UNORM = 48, + VIRGL_FORMAT_R16G16_UNORM = 49, + VIRGL_FORMAT_R16G16B16_UNORM = 50, + VIRGL_FORMAT_R16G16B16A16_UNORM = 51, + + VIRGL_FORMAT_R16_USCALED = 52, + VIRGL_FORMAT_R16G16_USCALED = 53, + VIRGL_FORMAT_R16G16B16_USCALED = 54, + VIRGL_FORMAT_R16G16B16A16_USCALED = 55, + + VIRGL_FORMAT_R16_SNORM = 56, + VIRGL_FORMAT_R16G16_SNORM = 57, + VIRGL_FORMAT_R16G16B16_SNORM = 58, + VIRGL_FORMAT_R16G16B16A16_SNORM = 59, + + VIRGL_FORMAT_R16_SSCALED = 60, + VIRGL_FORMAT_R16G16_SSCALED = 61, + VIRGL_FORMAT_R16G16B16_SSCALED = 62, + VIRGL_FORMAT_R16G16B16A16_SSCALED = 63, + + VIRGL_FORMAT_R8_UNORM = 64, + VIRGL_FORMAT_R8G8_UNORM = 65, + VIRGL_FORMAT_R8G8B8_UNORM = 66, + VIRGL_FORMAT_R8G8B8A8_UNORM = 67, + VIRGL_FORMAT_X8B8G8R8_UNORM = 68, + + VIRGL_FORMAT_R8_USCALED = 69, + VIRGL_FORMAT_R8G8_USCALED = 70, + VIRGL_FORMAT_R8G8B8_USCALED = 71, + VIRGL_FORMAT_R8G8B8A8_USCALED = 72, + + VIRGL_FORMAT_R8_SNORM = 74, + VIRGL_FORMAT_R8G8_SNORM = 75, + VIRGL_FORMAT_R8G8B8_SNORM = 76, + VIRGL_FORMAT_R8G8B8A8_SNORM = 77, + + VIRGL_FORMAT_R8_SSCALED = 82, + VIRGL_FORMAT_R8G8_SSCALED = 83, + VIRGL_FORMAT_R8G8B8_SSCALED = 84, + VIRGL_FORMAT_R8G8B8A8_SSCALED = 85, + + VIRGL_FORMAT_R32_FIXED = 87, + VIRGL_FORMAT_R32G32_FIXED = 88, + VIRGL_FORMAT_R32G32B32_FIXED = 89, + VIRGL_FORMAT_R32G32B32A32_FIXED = 90, + + VIRGL_FORMAT_R16_FLOAT = 91, + VIRGL_FORMAT_R16G16_FLOAT = 92, + VIRGL_FORMAT_R16G16B16_FLOAT = 93, + VIRGL_FORMAT_R16G16B16A16_FLOAT = 94, + + VIRGL_FORMAT_L8_SRGB = 95, + VIRGL_FORMAT_L8A8_SRGB = 96, + VIRGL_FORMAT_R8G8B8_SRGB = 97, + VIRGL_FORMAT_A8B8G8R8_SRGB = 98, + VIRGL_FORMAT_X8B8G8R8_SRGB = 99, + VIRGL_FORMAT_B8G8R8A8_SRGB = 100, + VIRGL_FORMAT_B8G8R8X8_SRGB = 101, + VIRGL_FORMAT_A8R8G8B8_SRGB = 102, + VIRGL_FORMAT_X8R8G8B8_SRGB = 103, + VIRGL_FORMAT_R8G8B8A8_SRGB = 104, + + /* compressed formats */ + VIRGL_FORMAT_DXT1_RGB = 105, + VIRGL_FORMAT_DXT1_RGBA = 106, + VIRGL_FORMAT_DXT3_RGBA = 107, + VIRGL_FORMAT_DXT5_RGBA = 108, + + /* sRGB, compressed */ + VIRGL_FORMAT_DXT1_SRGB = 109, + VIRGL_FORMAT_DXT1_SRGBA = 110, + VIRGL_FORMAT_DXT3_SRGBA = 111, + VIRGL_FORMAT_DXT5_SRGBA = 112, + + /* rgtc compressed */ + VIRGL_FORMAT_RGTC1_UNORM = 113, + VIRGL_FORMAT_RGTC1_SNORM = 114, + VIRGL_FORMAT_RGTC2_UNORM = 115, + VIRGL_FORMAT_RGTC2_SNORM = 116, + + VIRGL_FORMAT_R8G8_B8G8_UNORM = 117, + VIRGL_FORMAT_G8R8_G8B8_UNORM = 118, + + VIRGL_FORMAT_R8SG8SB8UX8U_NORM = 119, + VIRGL_FORMAT_R5SG5SB6U_NORM = 120, + + VIRGL_FORMAT_A8B8G8R8_UNORM = 121, + VIRGL_FORMAT_B5G5R5X1_UNORM = 122, + VIRGL_FORMAT_R10G10B10A2_USCALED = 123, + VIRGL_FORMAT_R11G11B10_FLOAT = 124, + VIRGL_FORMAT_R9G9B9E5_FLOAT = 125, + VIRGL_FORMAT_Z32_FLOAT_S8X24_UINT = 126, + VIRGL_FORMAT_R1_UNORM = 127, + VIRGL_FORMAT_R10G10B10X2_USCALED = 128, + VIRGL_FORMAT_R10G10B10X2_SNORM = 129, + + VIRGL_FORMAT_L4A4_UNORM = 130, + VIRGL_FORMAT_B10G10R10A2_UNORM = 131, + VIRGL_FORMAT_R10SG10SB10SA2U_NORM = 132, + VIRGL_FORMAT_R8G8Bx_SNORM = 133, + VIRGL_FORMAT_R8G8B8X8_UNORM = 134, + VIRGL_FORMAT_B4G4R4X4_UNORM = 135, + VIRGL_FORMAT_X24S8_UINT = 136, + VIRGL_FORMAT_S8X24_UINT = 137, + VIRGL_FORMAT_X32_S8X24_UINT = 138, + VIRGL_FORMAT_B2G3R3_UNORM = 139, + + VIRGL_FORMAT_L16A16_UNORM = 140, + VIRGL_FORMAT_A16_UNORM = 141, + VIRGL_FORMAT_I16_UNORM = 142, + + VIRGL_FORMAT_LATC1_UNORM = 143, + VIRGL_FORMAT_LATC1_SNORM = 144, + VIRGL_FORMAT_LATC2_UNORM = 145, + VIRGL_FORMAT_LATC2_SNORM = 146, + + VIRGL_FORMAT_A8_SNORM = 147, + VIRGL_FORMAT_L8_SNORM = 148, + VIRGL_FORMAT_L8A8_SNORM = 149, + VIRGL_FORMAT_I8_SNORM = 150, + VIRGL_FORMAT_A16_SNORM = 151, + VIRGL_FORMAT_L16_SNORM = 152, + VIRGL_FORMAT_L16A16_SNORM = 153, + VIRGL_FORMAT_I16_SNORM = 154, + + VIRGL_FORMAT_A16_FLOAT = 155, + VIRGL_FORMAT_L16_FLOAT = 156, + VIRGL_FORMAT_L16A16_FLOAT = 157, + VIRGL_FORMAT_I16_FLOAT = 158, + VIRGL_FORMAT_A32_FLOAT = 159, + VIRGL_FORMAT_L32_FLOAT = 160, + VIRGL_FORMAT_L32A32_FLOAT = 161, + VIRGL_FORMAT_I32_FLOAT = 162, + + VIRGL_FORMAT_YV12 = 163, + VIRGL_FORMAT_YV16 = 164, + VIRGL_FORMAT_IYUV = 165, /**< aka I420 */ + VIRGL_FORMAT_NV12 = 166, + VIRGL_FORMAT_NV21 = 167, + + VIRGL_FORMAT_A4R4_UNORM = 168, + VIRGL_FORMAT_R4A4_UNORM = 169, + VIRGL_FORMAT_R8A8_UNORM = 170, + VIRGL_FORMAT_A8R8_UNORM = 171, + + VIRGL_FORMAT_R10G10B10A2_SSCALED = 172, + VIRGL_FORMAT_R10G10B10A2_SNORM = 173, + VIRGL_FORMAT_B10G10R10A2_USCALED = 174, + VIRGL_FORMAT_B10G10R10A2_SSCALED = 175, + VIRGL_FORMAT_B10G10R10A2_SNORM = 176, + + VIRGL_FORMAT_R8_UINT = 177, + VIRGL_FORMAT_R8G8_UINT = 178, + VIRGL_FORMAT_R8G8B8_UINT = 179, + VIRGL_FORMAT_R8G8B8A8_UINT = 180, + + VIRGL_FORMAT_R8_SINT = 181, + VIRGL_FORMAT_R8G8_SINT = 182, + VIRGL_FORMAT_R8G8B8_SINT = 183, + VIRGL_FORMAT_R8G8B8A8_SINT = 184, + + VIRGL_FORMAT_R16_UINT = 185, + VIRGL_FORMAT_R16G16_UINT = 186, + VIRGL_FORMAT_R16G16B16_UINT = 187, + VIRGL_FORMAT_R16G16B16A16_UINT = 188, + + VIRGL_FORMAT_R16_SINT = 189, + VIRGL_FORMAT_R16G16_SINT = 190, + VIRGL_FORMAT_R16G16B16_SINT = 191, + VIRGL_FORMAT_R16G16B16A16_SINT = 192, + VIRGL_FORMAT_R32_UINT = 193, + VIRGL_FORMAT_R32G32_UINT = 194, + VIRGL_FORMAT_R32G32B32_UINT = 195, + VIRGL_FORMAT_R32G32B32A32_UINT = 196, + + VIRGL_FORMAT_R32_SINT = 197, + VIRGL_FORMAT_R32G32_SINT = 198, + VIRGL_FORMAT_R32G32B32_SINT = 199, + VIRGL_FORMAT_R32G32B32A32_SINT = 200, + + VIRGL_FORMAT_A8_UINT = 201, + VIRGL_FORMAT_I8_UINT = 202, + VIRGL_FORMAT_L8_UINT = 203, + VIRGL_FORMAT_L8A8_UINT = 204, + + VIRGL_FORMAT_A8_SINT = 205, + VIRGL_FORMAT_I8_SINT = 206, + VIRGL_FORMAT_L8_SINT = 207, + VIRGL_FORMAT_L8A8_SINT = 208, + + VIRGL_FORMAT_A16_UINT = 209, + VIRGL_FORMAT_I16_UINT = 210, + VIRGL_FORMAT_L16_UINT = 211, + VIRGL_FORMAT_L16A16_UINT = 212, + + VIRGL_FORMAT_A16_SINT = 213, + VIRGL_FORMAT_I16_SINT = 214, + VIRGL_FORMAT_L16_SINT = 215, + VIRGL_FORMAT_L16A16_SINT = 216, + + VIRGL_FORMAT_A32_UINT = 217, + VIRGL_FORMAT_I32_UINT = 218, + VIRGL_FORMAT_L32_UINT = 219, + VIRGL_FORMAT_L32A32_UINT = 220, + + VIRGL_FORMAT_A32_SINT = 221, + VIRGL_FORMAT_I32_SINT = 222, + VIRGL_FORMAT_L32_SINT = 223, + VIRGL_FORMAT_L32A32_SINT = 224, + + VIRGL_FORMAT_B10G10R10A2_UINT = 225, + VIRGL_FORMAT_ETC1_RGB8 = 226, + VIRGL_FORMAT_R8G8_R8B8_UNORM = 227, + VIRGL_FORMAT_G8R8_B8R8_UNORM = 228, + VIRGL_FORMAT_R8G8B8X8_SNORM = 229, + + VIRGL_FORMAT_R8G8B8X8_SRGB = 230, + + VIRGL_FORMAT_R8G8B8X8_UINT = 231, + VIRGL_FORMAT_R8G8B8X8_SINT = 232, + VIRGL_FORMAT_B10G10R10X2_UNORM = 233, + VIRGL_FORMAT_R16G16B16X16_UNORM = 234, + VIRGL_FORMAT_R16G16B16X16_SNORM = 235, + VIRGL_FORMAT_R16G16B16X16_FLOAT = 236, + VIRGL_FORMAT_R16G16B16X16_UINT = 237, + VIRGL_FORMAT_R16G16B16X16_SINT = 238, + VIRGL_FORMAT_R32G32B32X32_FLOAT = 239, + VIRGL_FORMAT_R32G32B32X32_UINT = 240, + VIRGL_FORMAT_R32G32B32X32_SINT = 241, + VIRGL_FORMAT_R8A8_SNORM = 242, + VIRGL_FORMAT_R16A16_UNORM = 243, + VIRGL_FORMAT_R16A16_SNORM = 244, + VIRGL_FORMAT_R16A16_FLOAT = 245, + VIRGL_FORMAT_R32A32_FLOAT = 246, + VIRGL_FORMAT_R8A8_UINT = 247, + VIRGL_FORMAT_R8A8_SINT = 248, + VIRGL_FORMAT_R16A16_UINT = 249, + VIRGL_FORMAT_R16A16_SINT = 250, + VIRGL_FORMAT_R32A32_UINT = 251, + VIRGL_FORMAT_R32A32_SINT = 252, + + VIRGL_FORMAT_R10G10B10A2_UINT = 253, + VIRGL_FORMAT_B5G6R5_SRGB = 254, + + VIRGL_FORMAT_BPTC_RGBA_UNORM = 255, + VIRGL_FORMAT_BPTC_SRGBA = 256, + VIRGL_FORMAT_BPTC_RGB_FLOAT = 257, + VIRGL_FORMAT_BPTC_RGB_UFLOAT = 258, + + VIRGL_FORMAT_A16L16_UNORM = 262, + + VIRGL_FORMAT_G8R8_UNORM = 263, + VIRGL_FORMAT_G8R8_SNORM = 264, + VIRGL_FORMAT_G16R16_UNORM = 265, + VIRGL_FORMAT_G16R16_SNORM = 266, + VIRGL_FORMAT_A8B8G8R8_SNORM = 267, + + VIRGL_FORMAT_A8L8_UNORM = 259, + VIRGL_FORMAT_A8L8_SNORM = 260, + VIRGL_FORMAT_A8L8_SRGB = 261, + + VIRGL_FORMAT_X8B8G8R8_SNORM = 268, + + VIRGL_FORMAT_R10G10B10X2_UNORM = 308, + VIRGL_FORMAT_A4B4G4R4_UNORM = 311, + + VIRGL_FORMAT_R8_SRGB = 312, + VIRGL_FORMAT_MAX /* = PIPE_FORMAT_COUNT */, + + /* Below formats must not be used in the guest. */ + VIRGL_FORMAT_B8G8R8X8_UNORM_EMULATED, + VIRGL_FORMAT_B8G8R8A8_UNORM_EMULATED, + VIRGL_FORMAT_MAX_EXTENDED +}; + +/* These are used by the capability_bits field in virgl_caps_v2. */ +#define VIRGL_CAP_NONE 0 +#define VIRGL_CAP_TGSI_INVARIANT (1 << 0) +#define VIRGL_CAP_TEXTURE_VIEW (1 << 1) +#define VIRGL_CAP_SET_MIN_SAMPLES (1 << 2) +#define VIRGL_CAP_COPY_IMAGE (1 << 3) +#define VIRGL_CAP_TGSI_PRECISE (1 << 4) +#define VIRGL_CAP_TXQS (1 << 5) +#define VIRGL_CAP_MEMORY_BARRIER (1 << 6) +#define VIRGL_CAP_COMPUTE_SHADER (1 << 7) +#define VIRGL_CAP_FB_NO_ATTACH (1 << 8) +#define VIRGL_CAP_ROBUST_BUFFER_ACCESS (1 << 9) +#define VIRGL_CAP_TGSI_FBFETCH (1 << 10) +#define VIRGL_CAP_SHADER_CLOCK (1 << 11) +#define VIRGL_CAP_TEXTURE_BARRIER (1 << 12) +#define VIRGL_CAP_TGSI_COMPONENTS (1 << 13) +#define VIRGL_CAP_GUEST_MAY_INIT_LOG (1 << 14) +#define VIRGL_CAP_SRGB_WRITE_CONTROL (1 << 15) +#define VIRGL_CAP_QBO (1 << 16) +#define VIRGL_CAP_TRANSFER (1 << 17) +#define VIRGL_CAP_FBO_MIXED_COLOR_FORMATS (1 << 18) +#define VIRGL_CAP_FAKE_FP64 (1 << 19) +#define VIRGL_CAP_BIND_COMMAND_ARGS (1 << 20) +#define VIRGL_CAP_MULTI_DRAW_INDIRECT (1 << 21) +#define VIRGL_CAP_INDIRECT_PARAMS (1 << 22) +#define VIRGL_CAP_TRANSFORM_FEEDBACK3 (1 << 23) +#define VIRGL_CAP_3D_ASTC (1 << 24) +#define VIRGL_CAP_INDIRECT_INPUT_ADDR (1 << 25) +#define VIRGL_CAP_COPY_TRANSFER (1 << 26) +#define VIRGL_CAP_CLIP_HALFZ (1 << 27) +#define VIRGL_CAP_APP_TWEAK_SUPPORT (1 << 28) +#define VIRGL_CAP_BGRA_SRGB_IS_EMULATED (1 << 29) + +/* virgl bind flags - these are compatible with mesa 10.5 gallium. + * but are fixed, no other should be passed to virgl either. + */ +#define VIRGL_BIND_DEPTH_STENCIL (1 << 0) +#define VIRGL_BIND_RENDER_TARGET (1 << 1) +#define VIRGL_BIND_SAMPLER_VIEW (1 << 3) +#define VIRGL_BIND_VERTEX_BUFFER (1 << 4) +#define VIRGL_BIND_INDEX_BUFFER (1 << 5) +#define VIRGL_BIND_CONSTANT_BUFFER (1 << 6) +#define VIRGL_BIND_DISPLAY_TARGET (1 << 7) +#define VIRGL_BIND_COMMAND_ARGS (1 << 8) +#define VIRGL_BIND_STREAM_OUTPUT (1 << 11) +#define VIRGL_BIND_SHADER_BUFFER (1 << 14) +#define VIRGL_BIND_QUERY_BUFFER (1 << 15) +#define VIRGL_BIND_CURSOR (1 << 16) +#define VIRGL_BIND_CUSTOM (1 << 17) +#define VIRGL_BIND_SCANOUT (1 << 18) +/* Used for buffers that are backed by guest storage and + * are only read by the host. + */ +#define VIRGL_BIND_STAGING (1 << 19) +#define VIRGL_BIND_SHARED (1 << 20) + +#define VIRGL_BIND_PREFER_EMULATED_BGRA (1 << 21) + +#define VIRGL_BIND_LINEAR (1 << 22) + +struct virgl_caps_bool_set1 { + unsigned indep_blend_enable:1; + unsigned indep_blend_func:1; + unsigned cube_map_array:1; + unsigned shader_stencil_export:1; + unsigned conditional_render:1; + unsigned start_instance:1; + unsigned primitive_restart:1; + unsigned blend_eq_sep:1; + unsigned instanceid:1; + unsigned vertex_element_instance_divisor:1; + unsigned seamless_cube_map:1; + unsigned occlusion_query:1; + unsigned timer_query:1; + unsigned streamout_pause_resume:1; + unsigned texture_multisample:1; + unsigned fragment_coord_conventions:1; + unsigned depth_clip_disable:1; + unsigned seamless_cube_map_per_texture:1; + unsigned ubo:1; + unsigned color_clamping:1; /* not in GL 3.1 core profile */ + unsigned poly_stipple:1; /* not in GL 3.1 core profile */ + unsigned mirror_clamp:1; + unsigned texture_query_lod:1; + unsigned has_fp64:1; + unsigned has_tessellation_shaders:1; + unsigned has_indirect_draw:1; + unsigned has_sample_shading:1; + unsigned has_cull:1; + unsigned conditional_render_inverted:1; + unsigned derivative_control:1; + unsigned polygon_offset_clamp:1; + unsigned transform_feedback_overflow_query:1; + /* DO NOT ADD ANYMORE MEMBERS - need to add another 32-bit to v2 caps */ +}; + +/* endless expansion capabilites - current gallium has 252 formats */ +struct virgl_supported_format_mask { + uint32_t bitmask[16]; +}; +/* capabilities set 2 - version 1 - 32-bit and float values */ +struct virgl_caps_v1 { + uint32_t max_version; + struct virgl_supported_format_mask sampler; + struct virgl_supported_format_mask render; + struct virgl_supported_format_mask depthstencil; + struct virgl_supported_format_mask vertexbuffer; + struct virgl_caps_bool_set1 bset; + uint32_t glsl_level; + uint32_t max_texture_array_layers; + uint32_t max_streamout_buffers; + uint32_t max_dual_source_render_targets; + uint32_t max_render_targets; + uint32_t max_samples; + uint32_t prim_mask; + uint32_t max_tbo_size; + uint32_t max_uniform_blocks; + uint32_t max_viewports; + uint32_t max_texture_gather_components; +}; + +/* + * This struct should be growable when used in capset 2, + * so we shouldn't have to add a v3 ever. + */ +struct virgl_caps_v2 { + struct virgl_caps_v1 v1; + float min_aliased_point_size; + float max_aliased_point_size; + float min_smooth_point_size; + float max_smooth_point_size; + float min_aliased_line_width; + float max_aliased_line_width; + float min_smooth_line_width; + float max_smooth_line_width; + float max_texture_lod_bias; + uint32_t max_geom_output_vertices; + uint32_t max_geom_total_output_components; + uint32_t max_vertex_outputs; + uint32_t max_vertex_attribs; + uint32_t max_shader_patch_varyings; + int32_t min_texel_offset; + int32_t max_texel_offset; + int32_t min_texture_gather_offset; + int32_t max_texture_gather_offset; + uint32_t texture_buffer_offset_alignment; + uint32_t uniform_buffer_offset_alignment; + uint32_t shader_buffer_offset_alignment; + uint32_t capability_bits; + uint32_t sample_locations[8]; + uint32_t max_vertex_attrib_stride; + uint32_t max_shader_buffer_frag_compute; + uint32_t max_shader_buffer_other_stages; + uint32_t max_shader_image_frag_compute; + uint32_t max_shader_image_other_stages; + uint32_t max_image_samples; + uint32_t max_compute_work_group_invocations; + uint32_t max_compute_shared_memory_size; + uint32_t max_compute_grid_size[3]; + uint32_t max_compute_block_size[3]; + uint32_t max_texture_2d_size; + uint32_t max_texture_3d_size; + uint32_t max_texture_cube_size; + uint32_t max_combined_shader_buffers; + uint32_t max_atomic_counters[6]; + uint32_t max_atomic_counter_buffers[6]; + uint32_t max_combined_atomic_counters; + uint32_t max_combined_atomic_counter_buffers; + uint32_t host_feature_check_version; + struct virgl_supported_format_mask supported_readback_formats; + struct virgl_supported_format_mask scanout; +}; + +union virgl_caps { + uint32_t max_version; + struct virgl_caps_v1 v1; + struct virgl_caps_v2 v2; +}; + +#define VIRGL_RESOURCE_Y_0_TOP (1 << 0) +#endif diff --git a/app/src/main/cpp/virglrenderer/src/virgl_protocol.h b/app/src/main/cpp/virglrenderer/src/virgl_protocol.h new file mode 100644 index 0000000..802f938 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/virgl_protocol.h @@ -0,0 +1,592 @@ +/* + * Copyright 2014, 2015 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef VIRGL_PROTOCOL_H +#define VIRGL_PROTOCOL_H + +#include + +#define VIRGL_QUERY_STATE_NEW 0 +#define VIRGL_QUERY_STATE_DONE 1 +#define VIRGL_QUERY_STATE_WAIT_HOST 2 + +#include + +struct virgl_host_query_state { + uint32_t query_state; + uint32_t result_size; + uint64_t result; +}; + +enum virgl_object_type { + VIRGL_OBJECT_NULL, + VIRGL_OBJECT_BLEND, + VIRGL_OBJECT_RASTERIZER, + VIRGL_OBJECT_DSA, + VIRGL_OBJECT_SHADER, + VIRGL_OBJECT_VERTEX_ELEMENTS, + VIRGL_OBJECT_SAMPLER_VIEW, + VIRGL_OBJECT_SAMPLER_STATE, + VIRGL_OBJECT_SURFACE, + VIRGL_OBJECT_QUERY, + VIRGL_OBJECT_STREAMOUT_TARGET, + VIRGL_MAX_OBJECTS, +}; + +/* context cmds to be encoded in the command stream */ +enum virgl_context_cmd { + VIRGL_CCMD_NOP = 0, + VIRGL_CCMD_CREATE_OBJECT = 1, + VIRGL_CCMD_BIND_OBJECT, + VIRGL_CCMD_DESTROY_OBJECT, + VIRGL_CCMD_SET_VIEWPORT_STATE, + VIRGL_CCMD_SET_FRAMEBUFFER_STATE, + VIRGL_CCMD_SET_VERTEX_BUFFERS, + VIRGL_CCMD_CLEAR, + VIRGL_CCMD_DRAW_VBO, + VIRGL_CCMD_RESOURCE_INLINE_WRITE, + VIRGL_CCMD_SET_SAMPLER_VIEWS, + VIRGL_CCMD_SET_INDEX_BUFFER, + VIRGL_CCMD_SET_CONSTANT_BUFFER, + VIRGL_CCMD_SET_STENCIL_REF, + VIRGL_CCMD_SET_BLEND_COLOR, + VIRGL_CCMD_SET_SCISSOR_STATE, + VIRGL_CCMD_BLIT, + VIRGL_CCMD_RESOURCE_COPY_REGION, + VIRGL_CCMD_BIND_SAMPLER_STATES, + VIRGL_CCMD_BEGIN_QUERY, + VIRGL_CCMD_END_QUERY, + VIRGL_CCMD_GET_QUERY_RESULT, + VIRGL_CCMD_SET_POLYGON_STIPPLE, + VIRGL_CCMD_SET_CLIP_STATE, + VIRGL_CCMD_SET_SAMPLE_MASK, + VIRGL_CCMD_SET_STREAMOUT_TARGETS, + VIRGL_CCMD_SET_RENDER_CONDITION, + VIRGL_CCMD_SET_UNIFORM_BUFFER, + + VIRGL_CCMD_SET_SUB_CTX, + VIRGL_CCMD_CREATE_SUB_CTX, + VIRGL_CCMD_DESTROY_SUB_CTX, + VIRGL_CCMD_BIND_SHADER, + VIRGL_CCMD_SET_TESS_STATE, + VIRGL_CCMD_SET_MIN_SAMPLES, + VIRGL_CCMD_SET_SHADER_BUFFERS, + VIRGL_CCMD_SET_SHADER_IMAGES, + VIRGL_CCMD_MEMORY_BARRIER, + VIRGL_CCMD_LAUNCH_GRID, + VIRGL_CCMD_SET_FRAMEBUFFER_STATE_NO_ATTACH, + VIRGL_CCMD_TEXTURE_BARRIER, + VIRGL_CCMD_SET_ATOMIC_BUFFERS, + VIRGL_CCMD_SET_DEBUG_FLAGS, + VIRGL_CCMD_GET_QUERY_RESULT_QBO, + VIRGL_CCMD_TRANSFER3D, + VIRGL_CCMD_END_TRANSFERS, + VIRGL_CCMD_COPY_TRANSFER3D, + VIRGL_CCMD_SET_TWEAKS, + VIRGL_MAX_COMMANDS +}; + +/* + 8-bit cmd headers + 8-bit object type + 16-bit length +*/ + +#define VIRGL_CMD0(cmd, obj, len) ((cmd) | ((obj) << 8) | ((len) << 16)) + +/* hw specification */ +#define VIRGL_MAX_COLOR_BUFS 8 +#define VIRGL_MAX_CLIP_PLANES 8 + +#define VIRGL_OBJ_CREATE_HEADER 0 +#define VIRGL_OBJ_CREATE_HANDLE 1 + +#define VIRGL_OBJ_BIND_HEADER 0 +#define VIRGL_OBJ_BIND_HANDLE 1 + +#define VIRGL_OBJ_DESTROY_HANDLE 1 + +/* some of these defines are a specification - not used in the code */ +/* bit offsets for blend state object */ +#define VIRGL_OBJ_BLEND_SIZE (VIRGL_MAX_COLOR_BUFS + 3) +#define VIRGL_OBJ_BLEND_HANDLE 1 +#define VIRGL_OBJ_BLEND_S0 2 +#define VIRGL_OBJ_BLEND_S0_INDEPENDENT_BLEND_ENABLE(x) ((x) & 0x1 << 0) +#define VIRGL_OBJ_BLEND_S0_LOGICOP_ENABLE(x) (((x) & 0x1) << 1) +#define VIRGL_OBJ_BLEND_S0_DITHER(x) (((x) & 0x1) << 2) +#define VIRGL_OBJ_BLEND_S0_ALPHA_TO_COVERAGE(x) (((x) & 0x1) << 3) +#define VIRGL_OBJ_BLEND_S0_ALPHA_TO_ONE(x) (((x) & 0x1) << 4) +#define VIRGL_OBJ_BLEND_S1 3 +#define VIRGL_OBJ_BLEND_S1_LOGICOP_FUNC(x) (((x) & 0xf) << 0) +/* repeated once per number of cbufs */ + +#define VIRGL_OBJ_BLEND_S2(cbuf) (4 + (cbuf)) +#define VIRGL_OBJ_BLEND_S2_RT_BLEND_ENABLE(x) (((x) & 0x1) << 0) +#define VIRGL_OBJ_BLEND_S2_RT_RGB_FUNC(x) (((x) & 0x7) << 1) +#define VIRGL_OBJ_BLEND_S2_RT_RGB_SRC_FACTOR(x) (((x) & 0x1f) << 4) +#define VIRGL_OBJ_BLEND_S2_RT_RGB_DST_FACTOR(x) (((x) & 0x1f) << 9) +#define VIRGL_OBJ_BLEND_S2_RT_ALPHA_FUNC(x) (((x) & 0x7) << 14) +#define VIRGL_OBJ_BLEND_S2_RT_ALPHA_SRC_FACTOR(x) (((x) & 0x1f) << 17) +#define VIRGL_OBJ_BLEND_S2_RT_ALPHA_DST_FACTOR(x) (((x) & 0x1f) << 22) +#define VIRGL_OBJ_BLEND_S2_RT_COLORMASK(x) (((x) & 0xf) << 27) + +/* bit offsets for DSA state */ +#define VIRGL_OBJ_DSA_SIZE 5 +#define VIRGL_OBJ_DSA_HANDLE 1 +#define VIRGL_OBJ_DSA_S0 2 +#define VIRGL_OBJ_DSA_S0_DEPTH_ENABLE(x) (((x) & 0x1) << 0) +#define VIRGL_OBJ_DSA_S0_DEPTH_WRITEMASK(x) (((x) & 0x1) << 1) +#define VIRGL_OBJ_DSA_S0_DEPTH_FUNC(x) (((x) & 0x7) << 2) +#define VIRGL_OBJ_DSA_S0_ALPHA_ENABLED(x) (((x) & 0x1) << 8) +#define VIRGL_OBJ_DSA_S0_ALPHA_FUNC(x) (((x) & 0x7) << 9) +#define VIRGL_OBJ_DSA_S1 3 +#define VIRGL_OBJ_DSA_S2 4 +#define VIRGL_OBJ_DSA_S1_STENCIL_ENABLED(x) (((x) & 0x1) << 0) +#define VIRGL_OBJ_DSA_S1_STENCIL_FUNC(x) (((x) & 0x7) << 1) +#define VIRGL_OBJ_DSA_S1_STENCIL_FAIL_OP(x) (((x) & 0x7) << 4) +#define VIRGL_OBJ_DSA_S1_STENCIL_ZPASS_OP(x) (((x) & 0x7) << 7) +#define VIRGL_OBJ_DSA_S1_STENCIL_ZFAIL_OP(x) (((x) & 0x7) << 10) +#define VIRGL_OBJ_DSA_S1_STENCIL_VALUEMASK(x) (((x) & 0xff) << 13) +#define VIRGL_OBJ_DSA_S1_STENCIL_WRITEMASK(x) (((x) & 0xff) << 21) +#define VIRGL_OBJ_DSA_ALPHA_REF 5 + +/* offsets for rasterizer state */ +#define VIRGL_OBJ_RS_SIZE 9 +#define VIRGL_OBJ_RS_HANDLE 1 +#define VIRGL_OBJ_RS_S0 2 +#define VIRGL_OBJ_RS_S0_FLATSHADE(x) (((x) & 0x1) << 0) +#define VIRGL_OBJ_RS_S0_DEPTH_CLIP(x) (((x) & 0x1) << 1) +#define VIRGL_OBJ_RS_S0_CLIP_HALFZ(x) (((x) & 0x1) << 2) +#define VIRGL_OBJ_RS_S0_RASTERIZER_DISCARD(x) (((x) & 0x1) << 3) +#define VIRGL_OBJ_RS_S0_FLATSHADE_FIRST(x) (((x) & 0x1) << 4) +#define VIRGL_OBJ_RS_S0_LIGHT_TWOSIZE(x) (((x) & 0x1) << 5) +#define VIRGL_OBJ_RS_S0_SPRITE_COORD_MODE(x) (((x) & 0x1) << 6) +#define VIRGL_OBJ_RS_S0_POINT_QUAD_RASTERIZATION(x) (((x) & 0x1) << 7) +#define VIRGL_OBJ_RS_S0_CULL_FACE(x) (((x) & 0x3) << 8) +#define VIRGL_OBJ_RS_S0_FILL_FRONT(x) (((x) & 0x3) << 10) +#define VIRGL_OBJ_RS_S0_FILL_BACK(x) (((x) & 0x3) << 12) +#define VIRGL_OBJ_RS_S0_SCISSOR(x) (((x) & 0x1) << 14) +#define VIRGL_OBJ_RS_S0_FRONT_CCW(x) (((x) & 0x1) << 15) +#define VIRGL_OBJ_RS_S0_CLAMP_VERTEX_COLOR(x) (((x) & 0x1) << 16) +#define VIRGL_OBJ_RS_S0_CLAMP_FRAGMENT_COLOR(x) (((x) & 0x1) << 17) +#define VIRGL_OBJ_RS_S0_OFFSET_LINE(x) (((x) & 0x1) << 18) +#define VIRGL_OBJ_RS_S0_OFFSET_POINT(x) (((x) & 0x1) << 19) +#define VIRGL_OBJ_RS_S0_OFFSET_TRI(x) (((x) & 0x1) << 20) +#define VIRGL_OBJ_RS_S0_POLY_SMOOTH(x) (((x) & 0x1) << 21) +#define VIRGL_OBJ_RS_S0_POLY_STIPPLE_ENABLE(x) (((x) & 0x1) << 22) +#define VIRGL_OBJ_RS_S0_POINT_SMOOTH(x) (((x) & 0x1) << 23) +#define VIRGL_OBJ_RS_S0_POINT_SIZE_PER_VERTEX(x) (((x) & 0x1) << 24) +#define VIRGL_OBJ_RS_S0_MULTISAMPLE(x) (((x) & 0x1) << 25) +#define VIRGL_OBJ_RS_S0_LINE_SMOOTH(x) (((x) & 0x1) << 26) +#define VIRGL_OBJ_RS_S0_LINE_STIPPLE_ENABLE(x) (((x) & 0x1) << 27) +#define VIRGL_OBJ_RS_S0_LINE_LAST_PIXEL(x) (((x) & 0x1) << 28) +#define VIRGL_OBJ_RS_S0_HALF_PIXEL_CENTER(x) (((x) & 0x1) << 29) +#define VIRGL_OBJ_RS_S0_BOTTOM_EDGE_RULE(x) (((x) & 0x1) << 30) +#define VIRGL_OBJ_RS_S0_FORCE_PERSAMPLE_INTERP(x) (((x) & 0x1) << 31) + +#define VIRGL_OBJ_RS_POINT_SIZE 3 +#define VIRGL_OBJ_RS_SPRITE_COORD_ENABLE 4 +#define VIRGL_OBJ_RS_S3 5 + +#define VIRGL_OBJ_RS_S3_LINE_STIPPLE_PATTERN(x) (((x) & 0xffff) << 0) +#define VIRGL_OBJ_RS_S3_LINE_STIPPLE_FACTOR(x) (((x) & 0xff) << 16) +#define VIRGL_OBJ_RS_S3_CLIP_PLANE_ENABLE(x) (((x) & 0xff) << 24) +#define VIRGL_OBJ_RS_LINE_WIDTH 6 +#define VIRGL_OBJ_RS_OFFSET_UNITS 7 +#define VIRGL_OBJ_RS_OFFSET_SCALE 8 +#define VIRGL_OBJ_RS_OFFSET_CLAMP 9 + +#define VIRGL_OBJ_CLEAR_SIZE 8 +#define VIRGL_OBJ_CLEAR_BUFFERS 1 +#define VIRGL_OBJ_CLEAR_COLOR_0 2 /* color is 4 * u32/f32/i32 */ +#define VIRGL_OBJ_CLEAR_COLOR_1 3 +#define VIRGL_OBJ_CLEAR_COLOR_2 4 +#define VIRGL_OBJ_CLEAR_COLOR_3 5 +#define VIRGL_OBJ_CLEAR_DEPTH_0 6 /* depth is a double precision float */ +#define VIRGL_OBJ_CLEAR_DEPTH_1 7 +#define VIRGL_OBJ_CLEAR_STENCIL 8 + +/* shader object */ +#define VIRGL_OBJ_SHADER_HDR_SIZE(nso) (5 + ((nso) ? (2 * nso) + 4 : 0)) +#define VIRGL_OBJ_SHADER_HANDLE 1 +#define VIRGL_OBJ_SHADER_TYPE 2 +#define VIRGL_OBJ_SHADER_OFFSET 3 +#define VIRGL_OBJ_SHADER_OFFSET_VAL(x) (((x) & 0x7fffffff) << 0) +/* start contains full length in VAL - also implies continuations */ +/* continuation contains offset in VAL */ +#define VIRGL_OBJ_SHADER_OFFSET_CONT (0x1u << 31) +#define VIRGL_OBJ_SHADER_NUM_TOKENS 4 +#define VIRGL_OBJ_SHADER_SO_NUM_OUTPUTS 5 +#define VIRGL_OBJ_SHADER_SO_STRIDE(x) (6 + (x)) +#define VIRGL_OBJ_SHADER_SO_OUTPUT0(x) (10 + (x * 2)) +#define VIRGL_OBJ_SHADER_SO_OUTPUT_REGISTER_INDEX(x) (((x) & 0xff) << 0) +#define VIRGL_OBJ_SHADER_SO_OUTPUT_START_COMPONENT(x) (((x) & 0x3) << 8) +#define VIRGL_OBJ_SHADER_SO_OUTPUT_NUM_COMPONENTS(x) (((x) & 0x7) << 10) +#define VIRGL_OBJ_SHADER_SO_OUTPUT_BUFFER(x) (((x) & 0x7) << 13) +#define VIRGL_OBJ_SHADER_SO_OUTPUT_DST_OFFSET(x) (((x) & 0xffff) << 16) +#define VIRGL_OBJ_SHADER_SO_OUTPUT0_SO(x) (11 + (x * 2)) +#define VIRGL_OBJ_SHADER_SO_OUTPUT_STREAM(x) (((x) & 0x03) << 0) + +/* viewport state */ +#define VIRGL_SET_VIEWPORT_STATE_SIZE(num_viewports) ((6 * num_viewports) + 1) +#define VIRGL_SET_VIEWPORT_START_SLOT 1 +#define VIRGL_SET_VIEWPORT_STATE_SCALE_0(x) (2 + (x * 6)) +#define VIRGL_SET_VIEWPORT_STATE_SCALE_1(x) (3 + (x * 6)) +#define VIRGL_SET_VIEWPORT_STATE_SCALE_2(x) (4 + (x * 6)) +#define VIRGL_SET_VIEWPORT_STATE_TRANSLATE_0(x) (5 + (x * 6)) +#define VIRGL_SET_VIEWPORT_STATE_TRANSLATE_1(x) (6 + (x * 6)) +#define VIRGL_SET_VIEWPORT_STATE_TRANSLATE_2(x) (7 + (x * 6)) + +/* framebuffer state */ +#define VIRGL_SET_FRAMEBUFFER_STATE_SIZE(nr_cbufs) (nr_cbufs + 2) +#define VIRGL_SET_FRAMEBUFFER_STATE_NR_CBUFS 1 +#define VIRGL_SET_FRAMEBUFFER_STATE_NR_ZSURF_HANDLE 2 +#define VIRGL_SET_FRAMEBUFFER_STATE_CBUF_HANDLE(x) ((x) + 3) + +/* vertex elements object */ +#define VIRGL_OBJ_VERTEX_ELEMENTS_SIZE(num_elements) (((num_elements) * 4) + 1) +#define VIRGL_OBJ_VERTEX_ELEMENTS_HANDLE 1 +#define VIRGL_OBJ_VERTEX_ELEMENTS_V0_SRC_OFFSET(x) (((x) * 4) + 2) /* repeated per VE */ +#define VIRGL_OBJ_VERTEX_ELEMENTS_V0_INSTANCE_DIVISOR(x) (((x) * 4) + 3) +#define VIRGL_OBJ_VERTEX_ELEMENTS_V0_VERTEX_BUFFER_INDEX(x) (((x) * 4) + 4) +#define VIRGL_OBJ_VERTEX_ELEMENTS_V0_SRC_FORMAT(x) (((x) * 4) + 5) + +/* vertex buffers */ +#define VIRGL_SET_VERTEX_BUFFERS_SIZE(num_buffers) ((num_buffers) * 3) +#define VIRGL_SET_VERTEX_BUFFER_STRIDE(x) (((x) * 3) + 1) +#define VIRGL_SET_VERTEX_BUFFER_OFFSET(x) (((x) * 3) + 2) +#define VIRGL_SET_VERTEX_BUFFER_HANDLE(x) (((x) * 3) + 3) + +/* index buffer */ +#define VIRGL_SET_INDEX_BUFFER_SIZE(ib) (((ib) ? 2 : 0) + 1) +#define VIRGL_SET_INDEX_BUFFER_HANDLE 1 +#define VIRGL_SET_INDEX_BUFFER_INDEX_SIZE 2 /* only if sending an IB handle */ +#define VIRGL_SET_INDEX_BUFFER_OFFSET 3 /* only if sending an IB handle */ + +/* constant buffer */ +#define VIRGL_SET_CONSTANT_BUFFER_SHADER_TYPE 1 +#define VIRGL_SET_CONSTANT_BUFFER_INDEX 2 +#define VIRGL_SET_CONSTANT_BUFFER_DATA_START 3 + +#define VIRGL_SET_UNIFORM_BUFFER_SIZE 5 +#define VIRGL_SET_UNIFORM_BUFFER_SHADER_TYPE 1 +#define VIRGL_SET_UNIFORM_BUFFER_INDEX 2 +#define VIRGL_SET_UNIFORM_BUFFER_OFFSET 3 +#define VIRGL_SET_UNIFORM_BUFFER_LENGTH 4 +#define VIRGL_SET_UNIFORM_BUFFER_RES_HANDLE 5 + +/* draw VBO */ +#define VIRGL_DRAW_VBO_SIZE 12 +#define VIRGL_DRAW_VBO_SIZE_TESS 14 +#define VIRGL_DRAW_VBO_SIZE_INDIRECT 20 +#define VIRGL_DRAW_VBO_START 1 +#define VIRGL_DRAW_VBO_COUNT 2 +#define VIRGL_DRAW_VBO_MODE 3 +#define VIRGL_DRAW_VBO_INDEXED 4 +#define VIRGL_DRAW_VBO_INSTANCE_COUNT 5 +#define VIRGL_DRAW_VBO_INDEX_BIAS 6 +#define VIRGL_DRAW_VBO_START_INSTANCE 7 +#define VIRGL_DRAW_VBO_PRIMITIVE_RESTART 8 +#define VIRGL_DRAW_VBO_RESTART_INDEX 9 +#define VIRGL_DRAW_VBO_MIN_INDEX 10 +#define VIRGL_DRAW_VBO_MAX_INDEX 11 +#define VIRGL_DRAW_VBO_COUNT_FROM_SO 12 +/* tess packet */ +#define VIRGL_DRAW_VBO_VERTICES_PER_PATCH 13 +#define VIRGL_DRAW_VBO_DRAWID 14 +/* indirect packet */ +#define VIRGL_DRAW_VBO_INDIRECT_HANDLE 15 +#define VIRGL_DRAW_VBO_INDIRECT_OFFSET 16 +#define VIRGL_DRAW_VBO_INDIRECT_STRIDE 17 +#define VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT 18 +#define VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT_OFFSET 19 +#define VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT_HANDLE 20 + +/* create surface */ +#define VIRGL_OBJ_SURFACE_SIZE 5 +#define VIRGL_OBJ_SURFACE_HANDLE 1 +#define VIRGL_OBJ_SURFACE_RES_HANDLE 2 +#define VIRGL_OBJ_SURFACE_FORMAT 3 +#define VIRGL_OBJ_SURFACE_BUFFER_FIRST_ELEMENT 4 +#define VIRGL_OBJ_SURFACE_BUFFER_LAST_ELEMENT 5 +#define VIRGL_OBJ_SURFACE_TEXTURE_LEVEL 4 +#define VIRGL_OBJ_SURFACE_TEXTURE_LAYERS 5 + +/* create streamout target */ +#define VIRGL_OBJ_STREAMOUT_SIZE 4 +#define VIRGL_OBJ_STREAMOUT_HANDLE 1 +#define VIRGL_OBJ_STREAMOUT_RES_HANDLE 2 +#define VIRGL_OBJ_STREAMOUT_BUFFER_OFFSET 3 +#define VIRGL_OBJ_STREAMOUT_BUFFER_SIZE 4 + +/* sampler state */ +#define VIRGL_OBJ_SAMPLER_STATE_SIZE 9 +#define VIRGL_OBJ_SAMPLER_STATE_HANDLE 1 +#define VIRGL_OBJ_SAMPLER_STATE_S0 2 +#define VIRGL_OBJ_SAMPLE_STATE_S0_WRAP_S(x) (((x) & 0x7) << 0) +#define VIRGL_OBJ_SAMPLE_STATE_S0_WRAP_T(x) (((x) & 0x7) << 3) +#define VIRGL_OBJ_SAMPLE_STATE_S0_WRAP_R(x) (((x) & 0x7) << 6) +#define VIRGL_OBJ_SAMPLE_STATE_S0_MIN_IMG_FILTER(x) (((x) & 0x3) << 9) +#define VIRGL_OBJ_SAMPLE_STATE_S0_MIN_MIP_FILTER(x) (((x) & 0x3) << 11) +#define VIRGL_OBJ_SAMPLE_STATE_S0_MAG_IMG_FILTER(x) (((x) & 0x3) << 13) +#define VIRGL_OBJ_SAMPLE_STATE_S0_COMPARE_MODE(x) (((x) & 0x1) << 15) +#define VIRGL_OBJ_SAMPLE_STATE_S0_COMPARE_FUNC(x) (((x) & 0x7) << 16) +#define VIRGL_OBJ_SAMPLE_STATE_S0_SEAMLESS_CUBE_MAP(x) (((x) & 0x1) << 19) + +#define VIRGL_OBJ_SAMPLER_STATE_LOD_BIAS 3 +#define VIRGL_OBJ_SAMPLER_STATE_MIN_LOD 4 +#define VIRGL_OBJ_SAMPLER_STATE_MAX_LOD 5 +#define VIRGL_OBJ_SAMPLER_STATE_BORDER_COLOR(x) ((x) + 6) /* 6 - 9 */ + + +/* sampler view */ +#define VIRGL_OBJ_SAMPLER_VIEW_SIZE 6 +#define VIRGL_OBJ_SAMPLER_VIEW_HANDLE 1 +#define VIRGL_OBJ_SAMPLER_VIEW_RES_HANDLE 2 +#define VIRGL_OBJ_SAMPLER_VIEW_FORMAT 3 +#define VIRGL_OBJ_SAMPLER_VIEW_BUFFER_FIRST_ELEMENT 4 +#define VIRGL_OBJ_SAMPLER_VIEW_BUFFER_LAST_ELEMENT 5 +#define VIRGL_OBJ_SAMPLER_VIEW_TEXTURE_LAYER 4 +#define VIRGL_OBJ_SAMPLER_VIEW_TEXTURE_LEVEL 5 +#define VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE 6 +#define VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE_R(x) (((x) & 0x7) << 0) +#define VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE_G(x) (((x) & 0x7) << 3) +#define VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE_B(x) (((x) & 0x7) << 6) +#define VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE_A(x) (((x) & 0x7) << 9) + +/* set sampler views */ +#define VIRGL_SET_SAMPLER_VIEWS_SIZE(num_views) ((num_views) + 2) +#define VIRGL_SET_SAMPLER_VIEWS_SHADER_TYPE 1 +#define VIRGL_SET_SAMPLER_VIEWS_START_SLOT 2 +#define VIRGL_SET_SAMPLER_VIEWS_V0_HANDLE 3 + +/* bind sampler states */ +#define VIRGL_BIND_SAMPLER_STATES(num_states) ((num_states) + 2) +#define VIRGL_BIND_SAMPLER_STATES_SHADER_TYPE 1 +#define VIRGL_BIND_SAMPLER_STATES_START_SLOT 2 +#define VIRGL_BIND_SAMPLER_STATES_S0_HANDLE 3 + +/* set stencil reference */ +#define VIRGL_SET_STENCIL_REF_SIZE 1 +#define VIRGL_SET_STENCIL_REF 1 +#define VIRGL_STENCIL_REF_VAL(f, s) ((f & 0xff) | (((s & 0xff) << 8))) + +/* set blend color */ +#define VIRGL_SET_BLEND_COLOR_SIZE 4 +#define VIRGL_SET_BLEND_COLOR(x) ((x) + 1) + +/* set scissor state */ +#define VIRGL_SET_SCISSOR_STATE_SIZE(x) (1 + 2 * x) +#define VIRGL_SET_SCISSOR_START_SLOT 1 +#define VIRGL_SET_SCISSOR_MINX_MINY(x) (2 + (x * 2)) +#define VIRGL_SET_SCISSOR_MAXX_MAXY(x) (3 + (x * 2)) + +/* resource copy region */ +#define VIRGL_CMD_RESOURCE_COPY_REGION_SIZE 13 +#define VIRGL_CMD_RCR_DST_RES_HANDLE 1 +#define VIRGL_CMD_RCR_DST_LEVEL 2 +#define VIRGL_CMD_RCR_DST_X 3 +#define VIRGL_CMD_RCR_DST_Y 4 +#define VIRGL_CMD_RCR_DST_Z 5 +#define VIRGL_CMD_RCR_SRC_RES_HANDLE 6 +#define VIRGL_CMD_RCR_SRC_LEVEL 7 +#define VIRGL_CMD_RCR_SRC_X 8 +#define VIRGL_CMD_RCR_SRC_Y 9 +#define VIRGL_CMD_RCR_SRC_Z 10 +#define VIRGL_CMD_RCR_SRC_W 11 +#define VIRGL_CMD_RCR_SRC_H 12 +#define VIRGL_CMD_RCR_SRC_D 13 + +/* blit */ +#define VIRGL_CMD_BLIT_SIZE 21 +#define VIRGL_CMD_BLIT_S0 1 +#define VIRGL_CMD_BLIT_S0_MASK(x) (((x) & 0xff) << 0) +#define VIRGL_CMD_BLIT_S0_FILTER(x) (((x) & 0x3) << 8) +#define VIRGL_CMD_BLIT_S0_SCISSOR_ENABLE(x) (((x) & 0x1) << 10) +#define VIRGL_CMD_BLIT_S0_RENDER_CONDITION_ENABLE(x) (((x) & 0x1) << 11) +#define VIRGL_CMD_BLIT_S0_ALPHA_BLEND(x) (((x) & 0x1) << 12) +#define VIRGL_CMD_BLIT_SCISSOR_MINX_MINY 2 +#define VIRGL_CMD_BLIT_SCISSOR_MAXX_MAXY 3 +#define VIRGL_CMD_BLIT_DST_RES_HANDLE 4 +#define VIRGL_CMD_BLIT_DST_LEVEL 5 +#define VIRGL_CMD_BLIT_DST_FORMAT 6 +#define VIRGL_CMD_BLIT_DST_X 7 +#define VIRGL_CMD_BLIT_DST_Y 8 +#define VIRGL_CMD_BLIT_DST_Z 9 +#define VIRGL_CMD_BLIT_DST_W 10 +#define VIRGL_CMD_BLIT_DST_H 11 +#define VIRGL_CMD_BLIT_DST_D 12 +#define VIRGL_CMD_BLIT_SRC_RES_HANDLE 13 +#define VIRGL_CMD_BLIT_SRC_LEVEL 14 +#define VIRGL_CMD_BLIT_SRC_FORMAT 15 +#define VIRGL_CMD_BLIT_SRC_X 16 +#define VIRGL_CMD_BLIT_SRC_Y 17 +#define VIRGL_CMD_BLIT_SRC_Z 18 +#define VIRGL_CMD_BLIT_SRC_W 19 +#define VIRGL_CMD_BLIT_SRC_H 20 +#define VIRGL_CMD_BLIT_SRC_D 21 + +/* query object */ +#define VIRGL_OBJ_QUERY_SIZE 4 +#define VIRGL_OBJ_QUERY_HANDLE 1 +#define VIRGL_OBJ_QUERY_TYPE_INDEX 2 +#define VIRGL_OBJ_QUERY_TYPE(x) (x & 0xffff) +#define VIRGL_OBJ_QUERY_INDEX(x) ((x & 0xffff) << 16) +#define VIRGL_OBJ_QUERY_OFFSET 3 +#define VIRGL_OBJ_QUERY_RES_HANDLE 4 + +#define VIRGL_QUERY_BEGIN_HANDLE 1 + +#define VIRGL_QUERY_END_HANDLE 1 + +#define VIRGL_QUERY_RESULT_HANDLE 1 +#define VIRGL_QUERY_RESULT_WAIT 2 + +/* render condition */ +#define VIRGL_RENDER_CONDITION_SIZE 3 +#define VIRGL_RENDER_CONDITION_HANDLE 1 +#define VIRGL_RENDER_CONDITION_CONDITION 2 +#define VIRGL_RENDER_CONDITION_MODE 3 + +/* resource inline write */ +#define VIRGL_RESOURCE_IW_RES_HANDLE 1 +#define VIRGL_RESOURCE_IW_LEVEL 2 +#define VIRGL_RESOURCE_IW_USAGE 3 +#define VIRGL_RESOURCE_IW_STRIDE 4 +#define VIRGL_RESOURCE_IW_LAYER_STRIDE 5 +#define VIRGL_RESOURCE_IW_X 6 +#define VIRGL_RESOURCE_IW_Y 7 +#define VIRGL_RESOURCE_IW_Z 8 +#define VIRGL_RESOURCE_IW_W 9 +#define VIRGL_RESOURCE_IW_H 10 +#define VIRGL_RESOURCE_IW_D 11 +#define VIRGL_RESOURCE_IW_DATA_START 12 + +/* set streamout targets */ +#define VIRGL_SET_STREAMOUT_TARGETS_APPEND_BITMASK 1 +#define VIRGL_SET_STREAMOUT_TARGETS_H0 2 + +/* set sample mask */ +#define VIRGL_SET_SAMPLE_MASK_SIZE 1 +#define VIRGL_SET_SAMPLE_MASK_MASK 1 + +/* set clip state */ +#define VIRGL_SET_CLIP_STATE_SIZE 32 +#define VIRGL_SET_CLIP_STATE_C0 1 + +/* polygon stipple */ +#define VIRGL_POLYGON_STIPPLE_SIZE 32 +#define VIRGL_POLYGON_STIPPLE_P0 1 + +#define VIRGL_BIND_SHADER_SIZE 2 +#define VIRGL_BIND_SHADER_HANDLE 1 +#define VIRGL_BIND_SHADER_TYPE 2 + +/* tess state */ +#define VIRGL_TESS_STATE_SIZE 6 + +/* set min samples */ +#define VIRGL_SET_MIN_SAMPLES_SIZE 1 +#define VIRGL_SET_MIN_SAMPLES_MASK 1 + +/* set shader buffers */ +#define VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE 3 +#define VIRGL_SET_SHADER_BUFFER_SIZE(x) (VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE * (x)) + 2 +#define VIRGL_SET_SHADER_BUFFER_SHADER_TYPE 1 +#define VIRGL_SET_SHADER_BUFFER_START_SLOT 2 +#define VIRGL_SET_SHADER_BUFFER_OFFSET(x) ((x) * VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE + 3) +#define VIRGL_SET_SHADER_BUFFER_LENGTH(x) ((x) * VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE + 4) +#define VIRGL_SET_SHADER_BUFFER_RES_HANDLE(x) ((x) * VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE + 5) + +/* set shader images */ +#define VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE 5 +#define VIRGL_SET_SHADER_IMAGE_SIZE(x) (VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE * (x)) + 2 +#define VIRGL_SET_SHADER_IMAGE_SHADER_TYPE 1 +#define VIRGL_SET_SHADER_IMAGE_START_SLOT 2 +#define VIRGL_SET_SHADER_IMAGE_FORMAT(x) ((x) * VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE + 3) +#define VIRGL_SET_SHADER_IMAGE_ACCESS(x) ((x) * VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE + 4) +#define VIRGL_SET_SHADER_IMAGE_LAYER_OFFSET(x) ((x) * VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE + 5) +#define VIRGL_SET_SHADER_IMAGE_LEVEL_SIZE(x) ((x) * VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE + 6) +#define VIRGL_SET_SHADER_IMAGE_RES_HANDLE(x) ((x) * VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE + 7) + +/* memory barrier */ +#define VIRGL_MEMORY_BARRIER_SIZE 1 +#define VIRGL_MEMORY_BARRIER_FLAGS 1 + +/* launch grid */ +#define VIRGL_LAUNCH_GRID_SIZE 8 +#define VIRGL_LAUNCH_BLOCK_X 1 +#define VIRGL_LAUNCH_BLOCK_Y 2 +#define VIRGL_LAUNCH_BLOCK_Z 3 +#define VIRGL_LAUNCH_GRID_X 4 +#define VIRGL_LAUNCH_GRID_Y 5 +#define VIRGL_LAUNCH_GRID_Z 6 +#define VIRGL_LAUNCH_INDIRECT_HANDLE 7 +#define VIRGL_LAUNCH_INDIRECT_OFFSET 8 + +/* framebuffer state no attachment */ +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_SIZE 2 +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_WIDTH_HEIGHT 1 +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_WIDTH(x) (x & 0xffff) +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_HEIGHT(x) ((x >> 16) & 0xffff) +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_LAYERS_SAMPLES 2 +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_LAYERS(x) (x & 0xffff) +#define VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_SAMPLES(x) ((x >> 16) & 0xff) + +/* texture barrier */ +#define VIRGL_TEXTURE_BARRIER_SIZE 1 +#define VIRGL_TEXTURE_BARRIER_FLAGS 1 + +/* hw atomics */ +#define VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE 3 +#define VIRGL_SET_ATOMIC_BUFFER_SIZE(x) (VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE * (x)) + 1 +#define VIRGL_SET_ATOMIC_BUFFER_START_SLOT 1 +#define VIRGL_SET_ATOMIC_BUFFER_OFFSET(x) ((x) * VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE + 2) +#define VIRGL_SET_ATOMIC_BUFFER_LENGTH(x) ((x) * VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE + 3) +#define VIRGL_SET_ATOMIC_BUFFER_RES_HANDLE(x) ((x) * VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE + 4) + +/* query buffer object */ +#define VIRGL_QUERY_RESULT_QBO_SIZE 6 +#define VIRGL_QUERY_RESULT_QBO_HANDLE 1 +#define VIRGL_QUERY_RESULT_QBO_QBO_HANDLE 2 +#define VIRGL_QUERY_RESULT_QBO_WAIT 3 +#define VIRGL_QUERY_RESULT_QBO_RESULT_TYPE 4 +#define VIRGL_QUERY_RESULT_QBO_OFFSET 5 +#define VIRGL_QUERY_RESULT_QBO_INDEX 6 + +#define VIRGL_TRANSFER_TO_HOST 1 +#define VIRGL_TRANSFER_FROM_HOST 2 + +/* Transfer */ +#define VIRGL_TRANSFER3D_SIZE 13 +/* The first 11 dwords are the same as VIRGL_RESOURCE_IW_* */ +#define VIRGL_TRANSFER3D_DATA_OFFSET 12 +#define VIRGL_TRANSFER3D_DIRECTION 13 + +/* Copy transfer */ +#define VIRGL_COPY_TRANSFER3D_SIZE 14 +/* The first 11 dwords are the same as VIRGL_RESOURCE_IW_* */ +#define VIRGL_COPY_TRANSFER3D_SRC_RES_HANDLE 12 +#define VIRGL_COPY_TRANSFER3D_SRC_RES_OFFSET 13 +#define VIRGL_COPY_TRANSFER3D_SYNCHRONIZED 14 + +#endif \ No newline at end of file diff --git a/app/src/main/cpp/virglrenderer/src/vrend_blitter.c b/app/src/main/cpp/virglrenderer/src/vrend_blitter.c new file mode 100644 index 0000000..31c3ce2 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_blitter.c @@ -0,0 +1,843 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +/* gallium blitter implementation in GL */ +/* for when we can't use glBlitFramebuffer */ +#include +#include "pipe/p_shader_tokens.h" + +#include "pipe/p_context.h" +#include "pipe/p_defines.h" +#include "pipe/p_screen.h" +#include "pipe/p_state.h" +#include "util/u_inlines.h" +#include "util/u_memory.h" +#include "util/u_dual_blend.h" + +#include "util/u_double_list.h" +#include "util/u_format.h" +#include "util/u_texture.h" +#include "tgsi/tgsi_parse.h" + +#include "vrend_object.h" +#include "vrend_shader.h" + +#include "vrend_renderer.h" + +#include "vrend_blitter.h" + +#define DEST_SWIZZLE_SNIPPET_SIZE 64 + +struct vrend_blitter_ctx { + virgl_gl_context gl_context; + bool initialised; + + GLuint vaoid; + + GLuint vs; + GLuint fs_texfetch_col[PIPE_MAX_TEXTURE_TYPES]; + GLuint fs_texfetch_depth[PIPE_MAX_TEXTURE_TYPES]; + GLuint fs_texfetch_depth_msaa[PIPE_MAX_TEXTURE_TYPES]; + GLuint fs_texfetch_col_swizzle; + GLuint fb_id; + + unsigned dst_width; + unsigned dst_height; + + GLuint vbo_id; + GLfloat vertices[4][2][4]; /**< {pos, color} or {pos, texcoord} */ +}; + +struct vrend_blitter_point { + int x; + int y; +}; + +struct vrend_blitter_delta { + int dx; + int dy; +}; + +static bool build_and_check(GLuint id, const char *buf) +{ + GLint param; + glShaderSource(id, 1, (const char **)&buf, NULL); + glCompileShader(id); + + glGetShaderiv(id, GL_COMPILE_STATUS, ¶m); + if (param == GL_FALSE) + return false; + return true; +} + +static bool blit_build_vs_passthrough(struct vrend_blitter_ctx *blit_ctx) +{ + blit_ctx->vs = glCreateShader(GL_VERTEX_SHADER); + + if (!build_and_check(blit_ctx->vs, VS_PASSTHROUGH_GLES)) { + glDeleteShader(blit_ctx->vs); + blit_ctx->vs = 0; + return false; + } + return true; +} + +static void create_dest_swizzle_snippet(const uint8_t swizzle[4], + char snippet[DEST_SWIZZLE_SNIPPET_SIZE]) +{ + static const uint8_t invalid_swizzle = 0xff; + ssize_t si = 0; + uint8_t inverse[4] = {invalid_swizzle, invalid_swizzle, invalid_swizzle, + invalid_swizzle}; + + for (int i = 0; i < 4; ++i) { + if (swizzle[i] > 3) continue; + if (inverse[swizzle[i]] == invalid_swizzle) + inverse[swizzle[i]] = i; + } + + for (int i = 0; i < 4; ++i) { + int res = -1; + if (inverse[i] > 3) { + /* Use 0.0f for unused color values, 1.0f for an unused alpha value */ + res = snprintf(&snippet[si], DEST_SWIZZLE_SNIPPET_SIZE - si, + i < 3 ? "0.0f, " : "1.0f"); + } else { + res = snprintf(&snippet[si], DEST_SWIZZLE_SNIPPET_SIZE - si, + "texel.%c%s", "rgba"[inverse[i]], i < 3 ? ", " : ""); + } + si += res > 0 ? res : 0; + } +} + +static const char *vec4_type_for_tgsi_ret(enum tgsi_return_type tgsi_ret) +{ + switch (tgsi_ret) { + case TGSI_RETURN_TYPE_SINT: return "ivec4"; + case TGSI_RETURN_TYPE_UINT: return "uvec4"; + default: return "vec4"; + } +} + +static enum tgsi_return_type tgsi_ret_for_format(enum virgl_formats format) +{ + if (util_format_is_pure_uint(format)) + return TGSI_RETURN_TYPE_UINT; + else if (util_format_is_pure_sint(format)) + return TGSI_RETURN_TYPE_SINT; + + return TGSI_RETURN_TYPE_UNORM; +} + +static GLuint blit_build_frag_tex_col(struct vrend_blitter_ctx *blit_ctx, + int tgsi_tex_target, + enum tgsi_return_type tgsi_ret, + const uint8_t swizzle[4]) +{ + GLuint fs_id; + char shader_buf[4096]; + const char *twm; + const char *ext_str = ""; + char dest_swizzle_snippet[DEST_SWIZZLE_SNIPPET_SIZE] = "texel"; + switch (tgsi_tex_target) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_BUFFER: + twm = ".x"; + break; + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_2D_MSAA: + default: + twm = ".xy"; + break; + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + twm = ".xyz"; + break; + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + twm = ""; + break; + } + + if (tgsi_tex_target == TGSI_TEXTURE_CUBE_ARRAY || + tgsi_tex_target == TGSI_TEXTURE_SHADOWCUBE_ARRAY) + ext_str = "#extension GL_ARB_texture_cube_map_array : require\n"; + + if (swizzle) + create_dest_swizzle_snippet(swizzle, dest_swizzle_snippet); + + snprintf(shader_buf, 4096, + tgsi_tex_target == TGSI_TEXTURE_1D ? FS_TEXFETCH_COL_GLES_1D : FS_TEXFETCH_COL_GLES, + ext_str, vec4_type_for_tgsi_ret(tgsi_ret), + vrend_shader_samplerreturnconv(tgsi_ret), + vrend_shader_samplertypeconv(tgsi_tex_target), twm, + dest_swizzle_snippet); + + fs_id = glCreateShader(GL_FRAGMENT_SHADER); + + if (!build_and_check(fs_id, shader_buf)) { + glDeleteShader(fs_id); + return 0; + } + + return fs_id; +} + +static GLuint blit_build_frag_tex_col_msaa(struct vrend_blitter_ctx *blit_ctx, + int tgsi_tex_target, + enum tgsi_return_type tgsi_ret, + const uint8_t swizzle[4], + int nr_samples) +{ + GLuint fs_id; + char shader_buf[4096]; + const char *twm; + const char *ivec; + char dest_swizzle_snippet[DEST_SWIZZLE_SNIPPET_SIZE] = "texel"; + const char *ext_str = ""; + + bool is_array = false; + switch (tgsi_tex_target) { + case TGSI_TEXTURE_2D_MSAA: + twm = ".xy"; + ivec = "ivec2"; + break; + case TGSI_TEXTURE_2D_ARRAY_MSAA: + twm = ".xyz"; + ivec = "ivec3"; + is_array = true; + break; + default: + return 0; + } + + if (swizzle) + create_dest_swizzle_snippet(swizzle, dest_swizzle_snippet); + + snprintf(shader_buf, 4096, + is_array ? FS_TEXFETCH_COL_MSAA_ARRAY_GLES : FS_TEXFETCH_COL_MSAA_GLES, + ext_str, vec4_type_for_tgsi_ret(tgsi_ret), + vrend_shader_samplerreturnconv(tgsi_ret), + vrend_shader_samplertypeconv(tgsi_tex_target), + nr_samples, ivec, twm, dest_swizzle_snippet); + + fs_id = glCreateShader(GL_FRAGMENT_SHADER); + + if (!build_and_check(fs_id, shader_buf)) { + glDeleteShader(fs_id); + return 0; + } + + return fs_id; +} + +static GLuint blit_build_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target) +{ + GLuint fs_id; + char shader_buf[4096]; + const char *twm; + + switch (tgsi_tex_target) { + case TGSI_TEXTURE_1D: + twm=".xy"; + break; + /* fallthrough */ + case TGSI_TEXTURE_BUFFER: + twm = ".x"; + break; + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_2D_MSAA: + default: + twm = ".xy"; + break; + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + twm = ".xyz"; + break; + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + twm = ""; + break; + } + + snprintf(shader_buf, 4096, FS_TEXFETCH_DS_GLES, + vrend_shader_samplertypeconv(tgsi_tex_target), twm); + + fs_id = glCreateShader(GL_FRAGMENT_SHADER); + + if (!build_and_check(fs_id, shader_buf)) { + glDeleteShader(fs_id); + return 0; + } + + return fs_id; +} + +static GLuint blit_build_frag_blit_msaa_depth(struct vrend_blitter_ctx *blit_ctx, int tgsi_tex_target) +{ + GLuint fs_id; + char shader_buf[4096]; + const char *twm; + const char *ivec; + + bool is_array = false; + switch (tgsi_tex_target) { + case TGSI_TEXTURE_2D_MSAA: + twm = ".xy"; + ivec = "ivec2"; + break; + case TGSI_TEXTURE_2D_ARRAY_MSAA: + twm = ".xyz"; + ivec = "ivec3"; + is_array = true; + break; + default: + return 0; + } + + snprintf(shader_buf, 4096, is_array ? FS_TEXFETCH_DS_MSAA_ARRAY_GLES : FS_TEXFETCH_DS_MSAA_GLES, + vrend_shader_samplertypeconv(tgsi_tex_target), ivec, twm); + + fs_id = glCreateShader(GL_FRAGMENT_SHADER); + + if (!build_and_check(fs_id, shader_buf)) { + glDeleteShader(fs_id); + return 0; + } + + return fs_id; +} + +static GLuint blit_get_frag_tex_writedepth(struct vrend_blitter_ctx *blit_ctx, int pipe_tex_target, unsigned nr_samples) +{ + assert(pipe_tex_target < PIPE_MAX_TEXTURE_TYPES); + + if (nr_samples > 0) { + GLuint *shader = &blit_ctx->fs_texfetch_depth_msaa[pipe_tex_target]; + + if (!*shader) { + unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, nr_samples); + + *shader = blit_build_frag_blit_msaa_depth(blit_ctx, tgsi_tex); + } + return *shader; + + } else { + GLuint *shader = &blit_ctx->fs_texfetch_depth[pipe_tex_target]; + + if (!*shader) { + unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, 0); + + *shader = blit_build_frag_tex_writedepth(blit_ctx, tgsi_tex); + } + return *shader; + } +} + +static GLuint blit_get_frag_tex_col(struct vrend_blitter_ctx *blit_ctx, + int pipe_tex_target, + unsigned nr_samples, + const struct vrend_format_table *src_entry, + const struct vrend_format_table *dst_entry, + bool skip_dest_swizzle) +{ + assert(pipe_tex_target < PIPE_MAX_TEXTURE_TYPES); + + bool needs_swizzle = !skip_dest_swizzle && (dst_entry->flags & VIRGL_TEXTURE_NEED_SWIZZLE); + + if (needs_swizzle || nr_samples > 1) { + const uint8_t *swizzle = needs_swizzle ? dst_entry->swizzle : NULL; + GLuint *shader = &blit_ctx->fs_texfetch_col_swizzle; + if (shader) { + glDeleteShader(*shader); + } + + unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, nr_samples); + enum tgsi_return_type tgsi_ret = tgsi_ret_for_format(src_entry->format); + + if (nr_samples > 0) { + // Integer textures are resolved using just one sample + int msaa_samples = tgsi_ret == TGSI_RETURN_TYPE_UNORM ? nr_samples : 1; + *shader = blit_build_frag_tex_col_msaa(blit_ctx, tgsi_tex, tgsi_ret, + swizzle, msaa_samples); + } else { + *shader = blit_build_frag_tex_col(blit_ctx, tgsi_tex, tgsi_ret, swizzle); + } + + return *shader; + } else { + GLuint *shader = &blit_ctx->fs_texfetch_col[pipe_tex_target]; + + if (!*shader) { + unsigned tgsi_tex = util_pipe_tex_to_tgsi_tex(pipe_tex_target, 0); + enum tgsi_return_type tgsi_ret = tgsi_ret_for_format(src_entry->format); + + *shader = blit_build_frag_tex_col(blit_ctx, tgsi_tex, tgsi_ret, NULL); + } + return *shader; + } +} + +static void vrend_renderer_init_blit_ctx(struct virgl_client *client, struct vrend_blitter_ctx *blit_ctx) +{ + int i; + if (blit_ctx->initialised) { + vrend_clicbs->make_current(client, blit_ctx->gl_context); + return; + } + + blit_ctx->initialised = true; + + blit_ctx->gl_context = vrend_clicbs->create_gl_context(client); + + vrend_clicbs->make_current(client, blit_ctx->gl_context); + glGenVertexArrays(1, &blit_ctx->vaoid); + glGenFramebuffers(1, &blit_ctx->fb_id); + + glGenBuffers(1, &blit_ctx->vbo_id); + blit_build_vs_passthrough(blit_ctx); + + for (i = 0; i < 4; i++) + blit_ctx->vertices[i][0][3] = 1; /*v.w*/ + glBindVertexArray(blit_ctx->vaoid); + glBindBuffer(GL_ARRAY_BUFFER, blit_ctx->vbo_id); +} + +static inline GLenum convert_mag_filter(unsigned int filter) +{ + if (filter == PIPE_TEX_FILTER_NEAREST) + return GL_NEAREST; + return GL_LINEAR; +} + +static void blitter_set_dst_dim(struct vrend_blitter_ctx *blit_ctx, + unsigned width, unsigned height) +{ + blit_ctx->dst_width = width; + blit_ctx->dst_height = height; +} + +static void blitter_set_rectangle(struct vrend_blitter_ctx *blit_ctx, + int x1, int y1, int x2, int y2, + float depth) +{ + int i; + + /* set vertex positions */ + blit_ctx->vertices[0][0][0] = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v0.x*/ + blit_ctx->vertices[0][0][1] = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v0.y*/ + + blit_ctx->vertices[1][0][0] = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v1.x*/ + blit_ctx->vertices[1][0][1] = (float)y1 / blit_ctx->dst_height * 2.0f - 1.0f; /*v1.y*/ + + blit_ctx->vertices[2][0][0] = (float)x2 / blit_ctx->dst_width * 2.0f - 1.0f; /*v2.x*/ + blit_ctx->vertices[2][0][1] = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v2.y*/ + + blit_ctx->vertices[3][0][0] = (float)x1 / blit_ctx->dst_width * 2.0f - 1.0f; /*v3.x*/ + blit_ctx->vertices[3][0][1] = (float)y2 / blit_ctx->dst_height * 2.0f - 1.0f; /*v3.y*/ + + for (i = 0; i < 4; i++) + blit_ctx->vertices[i][0][2] = depth; /*z*/ + + glViewport(0, 0, blit_ctx->dst_width, blit_ctx->dst_height); +} + +static void get_texcoords(struct vrend_blitter_ctx *blit_ctx, + struct vrend_resource *src_res, + int src_level, + int x1, int y1, int x2, int y2, + float out[4]) +{ + bool normalized = src_res->base.nr_samples < 1; + + if (normalized) { + out[0] = x1 / (float)u_minify(src_res->base.width0, src_level); + out[1] = y1 / (float)u_minify(src_res->base.height0, src_level); + out[2] = x2 / (float)u_minify(src_res->base.width0, src_level); + out[3] = y2 / (float)u_minify(src_res->base.height0, src_level); + } else { + out[0] = (float) x1; + out[1] = (float) y1; + out[2] = (float) x2; + out[3] = (float) y2; + } +} +static void set_texcoords_in_vertices(const float coord[4], + float *out, unsigned stride) +{ + out[0] = coord[0]; /*t0.s*/ + out[1] = coord[1]; /*t0.t*/ + out += stride; + out[0] = coord[2]; /*t1.s*/ + out[1] = coord[1]; /*t1.t*/ + out += stride; + out[0] = coord[2]; /*t2.s*/ + out[1] = coord[3]; /*t2.t*/ + out += stride; + out[0] = coord[0]; /*t3.s*/ + out[1] = coord[3]; /*t3.t*/ +} + +static void blitter_set_texcoords(struct vrend_blitter_ctx *blit_ctx, + struct vrend_resource *src_res, + int level, + float layer, unsigned sample, + int x1, int y1, int x2, int y2) +{ + float coord[4]; + float face_coord[4][2]; + int i; + get_texcoords(blit_ctx, src_res, level, x1, y1, x2, y2, coord); + + if (src_res->base.target == PIPE_TEXTURE_CUBE || + src_res->base.target == PIPE_TEXTURE_CUBE_ARRAY) { + set_texcoords_in_vertices(coord, &face_coord[0][0], 2); + util_map_texcoords2d_onto_cubemap((unsigned)layer % 6, + /* pointer, stride in floats */ + &face_coord[0][0], 2, + &blit_ctx->vertices[0][1][0], 8, + FALSE); + } else { + set_texcoords_in_vertices(coord, &blit_ctx->vertices[0][1][0], 8); + } + + switch (src_res->base.target) { + case PIPE_TEXTURE_3D: + { + float r = layer / (float)u_minify(src_res->base.depth0, + level); + for (i = 0; i < 4; i++) + blit_ctx->vertices[i][1][2] = r; /*r*/ + } + break; + + case PIPE_TEXTURE_1D_ARRAY: + for (i = 0; i < 4; i++) + blit_ctx->vertices[i][1][1] = (float) layer; /*t*/ + break; + + case PIPE_TEXTURE_2D_ARRAY: + for (i = 0; i < 4; i++) { + blit_ctx->vertices[i][1][2] = (float) layer; /*r*/ + blit_ctx->vertices[i][1][3] = (float) sample; /*q*/ + } + break; + case PIPE_TEXTURE_CUBE_ARRAY: + for (i = 0; i < 4; i++) + blit_ctx->vertices[i][1][3] = (float) ((unsigned)layer / 6); /*w*/ + break; + case PIPE_TEXTURE_2D: + for (i = 0; i < 4; i++) { + blit_ctx->vertices[i][1][3] = (float) sample; /*r*/ + } + break; + default:; + } +} + +static void set_dsa_write_depth_keep_stencil(void) +{ + glDisable(GL_STENCIL_TEST); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); +} + +static inline GLenum to_gl_swizzle(int swizzle) +{ + switch (swizzle) { + case PIPE_SWIZZLE_RED: return GL_RED; + case PIPE_SWIZZLE_GREEN: return GL_GREEN; + case PIPE_SWIZZLE_BLUE: return GL_BLUE; + case PIPE_SWIZZLE_ALPHA: return GL_ALPHA; + case PIPE_SWIZZLE_ZERO: return GL_ZERO; + case PIPE_SWIZZLE_ONE: return GL_ONE; + default: + return 0; + } +} + +/* Calculate the delta required to keep 'v' within [0, max] */ +static int calc_delta_for_bound(int v, int max) +{ + int delta = 0; + + if (v < 0) + delta = -v; + else if (v > max) + delta = - (v - max); + + return delta; +} + +/* Calculate the deltas for the source blit region points in order to bound + * them within the source resource extents */ +static void calc_src_deltas_for_bounds(struct vrend_resource *src_res, + const struct pipe_blit_info *info, + struct vrend_blitter_delta *src0_delta, + struct vrend_blitter_delta *src1_delta) +{ + int max_x = u_minify(src_res->base.width0, info->src.level) - 1; + int max_y = u_minify(src_res->base.height0, info->src.level) - 1; + + /* Whether the bounds for the coordinates of a point are inclusive or + * exclusive depends on the direction of the blit read. Adjust the max + * bounds accordingly, with an adjustment of 0 for inclusive, and 1 for + * exclusive. */ + int src0_x_excl = info->src.box.width < 0; + int src0_y_excl = info->src.box.height < 0; + + src0_delta->dx = calc_delta_for_bound(info->src.box.x, max_x + src0_x_excl); + src0_delta->dy = calc_delta_for_bound(info->src.box.y, max_y + src0_y_excl); + + src1_delta->dx = calc_delta_for_bound(info->src.box.x + info->src.box.width, + max_x + !src0_x_excl); + src1_delta->dy = calc_delta_for_bound(info->src.box.y + info->src.box.height, + max_y + !src0_y_excl); +} + +/* Calculate dst delta values to adjust the dst points for any changes in the + * src points */ +static void calc_dst_deltas_from_src(const struct pipe_blit_info *info, + const struct vrend_blitter_delta *src0_delta, + const struct vrend_blitter_delta *src1_delta, + struct vrend_blitter_delta *dst0_delta, + struct vrend_blitter_delta *dst1_delta) +{ + float scale_x = (float)info->dst.box.width / (float)info->src.box.width; + float scale_y = (float)info->dst.box.height / (float)info->src.box.height; + + dst0_delta->dx = src0_delta->dx * scale_x; + dst0_delta->dy = src0_delta->dy * scale_y; + + dst1_delta->dx = src1_delta->dx * scale_x; + dst1_delta->dy = src1_delta->dy * scale_y; +} + +/* implement blitting using OpenGL. */ +void vrend_renderer_blit_gl(struct virgl_client *client, + struct vrend_resource *src_res, + struct vrend_resource *dst_res, + GLenum blit_views[2], + const struct pipe_blit_info *info, + bool has_texture_srgb_decode, + bool has_srgb_write_control, + bool skip_dest_swizzle) +{ + struct vrend_blitter_ctx *blit_ctx; + + if (!client->vrend_blit_ctx) + client->vrend_blit_ctx = CALLOC_STRUCT(vrend_blitter_ctx); + + blit_ctx = client->vrend_blit_ctx; + + GLuint buffers; + GLuint prog_id; + GLuint fs_id; + GLint lret; + GLenum filter; + GLuint pos_loc, tc_loc; + GLuint samp_loc; + bool has_depth, has_stencil; + bool blit_stencil, blit_depth; + int dst_z; + struct vrend_blitter_delta src0_delta, src1_delta, dst0_delta, dst1_delta; + struct vrend_blitter_point src0, src1, dst0, dst1; + const struct util_format_description *src_desc = + util_format_description(src_res->base.format); + const struct util_format_description *dst_desc = + util_format_description(dst_res->base.format); + const struct vrend_format_table *src_entry = vrend_get_format_table_entry(info->src.format); + const struct vrend_format_table *dst_entry = vrend_get_format_table_entry(info->dst.format); + + has_depth = util_format_has_depth(src_desc) && + util_format_has_depth(dst_desc); + has_stencil = util_format_has_stencil(src_desc) && + util_format_has_stencil(dst_desc); + + blit_depth = has_depth && (info->mask & PIPE_MASK_Z); + blit_stencil = has_stencil && (info->mask & PIPE_MASK_S) & 0; + + filter = convert_mag_filter(info->filter); + vrend_renderer_init_blit_ctx(client, blit_ctx); + + blitter_set_dst_dim(blit_ctx, + u_minify(dst_res->base.width0, info->dst.level), + u_minify(dst_res->base.height0, info->dst.level)); + + /* Calculate src and dst points taking deltas into account */ + calc_src_deltas_for_bounds(src_res, info, &src0_delta, &src1_delta); + calc_dst_deltas_from_src(info, &src0_delta, &src1_delta, &dst0_delta, &dst1_delta); + + src0.x = info->src.box.x + src0_delta.dx; + src0.y = info->src.box.y + src0_delta.dy; + src1.x = info->src.box.x + info->src.box.width + src1_delta.dx; + src1.y = info->src.box.y + info->src.box.height + src1_delta.dy; + + dst0.x = info->dst.box.x + dst0_delta.dx; + dst0.y = info->dst.box.y + dst0_delta.dy; + dst1.x = info->dst.box.x + info->dst.box.width + dst1_delta.dx; + dst1.y = info->dst.box.y + info->dst.box.height + dst1_delta.dy; + + blitter_set_rectangle(blit_ctx, dst0.x, dst0.y, dst1.x, dst1.y, 0); + + prog_id = glCreateProgram(); + glAttachShader(prog_id, blit_ctx->vs); + + if (blit_depth || blit_stencil) { + fs_id = blit_get_frag_tex_writedepth(blit_ctx, src_res->base.target, + src_res->base.nr_samples); + } else { + fs_id = blit_get_frag_tex_col(blit_ctx, src_res->base.target, + src_res->base.nr_samples, + src_entry, dst_entry, + skip_dest_swizzle); + } + glAttachShader(prog_id, fs_id); + + glLinkProgram(prog_id); + glGetProgramiv(prog_id, GL_LINK_STATUS, &lret); + if (lret == GL_FALSE) { + glDeleteProgram(prog_id); + return; + } + + glUseProgram(prog_id); + + glBindFramebuffer(GL_FRAMEBUFFER, blit_ctx->fb_id); + vrend_fb_bind_texture_id(dst_res, blit_views[1], 0, info->dst.level, info->dst.box.z); + + buffers = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &buffers); + + glBindTexture(src_res->target, blit_views[0]); + + if (src_entry->flags & VIRGL_TEXTURE_NEED_SWIZZLE) { + glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_R, + to_gl_swizzle(src_entry->swizzle[0])); + glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_G, + to_gl_swizzle(src_entry->swizzle[1])); + glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_B, + to_gl_swizzle(src_entry->swizzle[2])); + glTexParameteri(src_res->target, GL_TEXTURE_SWIZZLE_A, + to_gl_swizzle(src_entry->swizzle[3])); + } + + /* Just make sure that no stale state disabled decoding */ + if (has_texture_srgb_decode && util_format_is_srgb(info->src.format) && + src_res->base.nr_samples < 1) + glTexParameteri(src_res->target, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT); + + if (src_res->base.nr_samples < 1) { + glTexParameteri(src_res->target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(src_res->target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(src_res->target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + glTexParameteri(src_res->target, GL_TEXTURE_BASE_LEVEL, info->src.level); + glTexParameteri(src_res->target, GL_TEXTURE_MAX_LEVEL, info->src.level); + + if (src_res->base.nr_samples < 1) { + glTexParameterf(src_res->target, GL_TEXTURE_MAG_FILTER, filter); + glTexParameterf(src_res->target, GL_TEXTURE_MIN_FILTER, filter); + } + pos_loc = glGetAttribLocation(prog_id, "arg0"); + tc_loc = glGetAttribLocation(prog_id, "arg1"); + samp_loc = glGetUniformLocation(prog_id, "samp"); + + glUniform1i(samp_loc, 0); + + glVertexAttribPointer(pos_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0); + glVertexAttribPointer(tc_loc, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)(4 * sizeof(float))); + + glEnableVertexAttribArray(pos_loc); + glEnableVertexAttribArray(tc_loc); + + set_dsa_write_depth_keep_stencil(); + + if (info->scissor_enable) { + glScissor(info->scissor.minx, info->scissor.miny, info->scissor.maxx - info->scissor.minx, info->scissor.maxy - info->scissor.miny); + glEnable(GL_SCISSOR_TEST); + } else + glDisable(GL_SCISSOR_TEST); + + for (dst_z = 0; dst_z < info->dst.box.depth; dst_z++) { + float dst2src_scale = info->src.box.depth / (float)info->dst.box.depth; + float dst_offset = ((info->src.box.depth - 1) - + (info->dst.box.depth - 1) * dst2src_scale) * 0.5; + float src_z = (dst_z + dst_offset) * dst2src_scale; + uint32_t layer = (dst_res->target == GL_TEXTURE_CUBE_MAP) ? info->dst.box.z : dst_z; + + glBindFramebuffer(GL_FRAMEBUFFER, blit_ctx->fb_id); + vrend_fb_bind_texture_id(dst_res, blit_views[1], 0, info->dst.level, layer); + + buffers = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &buffers); + blitter_set_texcoords(blit_ctx, src_res, info->src.level, + info->src.box.z + src_z, 0, + src0.x, src0.y, src1.x, src1.y); + + glBufferData(GL_ARRAY_BUFFER, sizeof(blit_ctx->vertices), blit_ctx->vertices, GL_STATIC_DRAW); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + + glUseProgram(0); + glDeleteProgram(prog_id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + glBindTexture(src_res->target, 0); +} + +void vrend_blitter_fini(struct virgl_client *client) +{ + if (!client->vrend_blit_ctx) + return; + + client->vrend_blit_ctx->initialised = false; + vrend_clicbs->destroy_gl_context(client, client->vrend_blit_ctx->gl_context); + memset(client->vrend_blit_ctx, 0, sizeof(client->vrend_blit_ctx)); + client->vrend_blit_ctx = NULL; +} diff --git a/app/src/main/cpp/virglrenderer/src/vrend_blitter.h b/app/src/main/cpp/virglrenderer/src/vrend_blitter.h new file mode 100644 index 0000000..9c079fa --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_blitter.h @@ -0,0 +1,114 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#ifndef VREND_BLITTER_H +#define VREND_BLITTER_H + +/* shaders for blitting */ + +#define HEADER_GLES \ + "// Blitter\n" \ + "#version 310 es\n" \ + "precision mediump float;\n" \ + +#define HEADER_GLES_MS_ARRAY \ + "// Blitter\n" \ + "#version 310 es\n" \ + "#extension GL_OES_texture_storage_multisample_2d_array: require\n" \ + "precision mediump float;\n" \ + +#define VS_PASSTHROUGH_BODY \ + "in vec4 arg0;\n" \ + "in vec4 arg1;\n" \ + "out vec4 tc;\n" \ + "void main() {\n" \ + " gl_Position = arg0;\n" \ + " tc = arg1;\n" \ + "}\n" + +#define VS_PASSTHROUGH_GLES HEADER_GLES VS_PASSTHROUGH_BODY + +#define FS_TEXFETCH_COL_BODY \ + "%s" \ + "#define cvec4 %s\n" \ + "uniform mediump %csampler%s samp;\n" \ + "in vec4 tc;\n" \ + "out cvec4 FragColor;\n" \ + "void main() {\n" \ + " cvec4 texel = texture(samp, tc%s);\n" \ + " FragColor = cvec4(%s);\n" \ + "}\n" + +#define FS_TEXFETCH_COL_GLES_1D_BODY \ + "%s" \ + "#define cvec4 %s\n" \ + "uniform mediump %csampler%s samp;\n" \ + "in vec4 tc;\n" \ + "out cvec4 FragColor;\n" \ + "void main() {\n" \ + " cvec4 texel = texture(samp, vec2(tc%s, 0.5));\n" \ + " FragColor = cvec4(%s);\n" \ + "}\n" + +#define FS_TEXFETCH_COL_GLES HEADER_GLES FS_TEXFETCH_COL_BODY +#define FS_TEXFETCH_COL_GLES_1D HEADER_GLES FS_TEXFETCH_COL_GLES_1D_BODY + +#define FS_TEXFETCH_COL_MSAA_BODY \ + "%s" \ + "#define cvec4 %s\n" \ + "uniform mediump %csampler%s samp;\n" \ + "in vec4 tc;\n" \ + "out cvec4 FragColor;\n" \ + "void main() {\n" \ + " const int num_samples = %d;\n" \ + " cvec4 texel = cvec4(0);\n" \ + " for (int i = 0; i < num_samples; ++i) \n" \ + " texel += texelFetch(samp, %s(tc%s), i);\n" \ + " texel = texel / cvec4(num_samples);\n" \ + " FragColor = cvec4(%s);\n" \ + "}\n" + +#define FS_TEXFETCH_COL_MSAA_GLES HEADER_GLES FS_TEXFETCH_COL_MSAA_BODY +#define FS_TEXFETCH_COL_MSAA_ARRAY_GLES HEADER_GLES_MS_ARRAY FS_TEXFETCH_COL_MSAA_BODY + +#define FS_TEXFETCH_DS_BODY \ + "uniform mediump sampler%s samp;\n" \ + "in vec4 tc;\n" \ + "void main() {\n" \ + " gl_FragDepth = float(texture(samp, tc%s).x);\n" \ + "}\n" + +#define FS_TEXFETCH_DS_GLES HEADER_GLES FS_TEXFETCH_DS_BODY + +#define FS_TEXFETCH_DS_MSAA_BODY_GLES \ + "uniform mediump sampler%s samp;\n" \ + "in vec4 tc;\n" \ + "void main() {\n" \ + " gl_FragDepth = float(texelFetch(samp, %s(tc%s), int(tc.z)).x);\n" \ + "}\n" + + +#define FS_TEXFETCH_DS_MSAA_GLES HEADER_GLES FS_TEXFETCH_DS_MSAA_BODY_GLES +#define FS_TEXFETCH_DS_MSAA_ARRAY_GLES HEADER_GLES_MS_ARRAY FS_TEXFETCH_DS_MSAA_BODY_GLES + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/vrend_decode.c b/app/src/main/cpp/virglrenderer/src/vrend_decode.c new file mode 100644 index 0000000..3f8839f --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_decode.c @@ -0,0 +1,1551 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#include +#include +#include +#include + +#include "util/u_memory.h" +#include "pipe/p_defines.h" +#include "pipe/p_state.h" +#include "pipe/p_shader_tokens.h" +#include "vrend_renderer.h" +#include "vrend_object.h" +#include "tgsi/tgsi_text.h" + +struct vrend_decoder_state { + uint32_t *buf; + uint32_t buf_total; + uint32_t buf_offset; +}; + +struct vrend_decode_ctx { + struct vrend_decoder_state ids, *ds; + struct vrend_context *grctx; +}; + +static inline uint32_t get_buf_entry(struct vrend_decode_ctx *ctx, uint32_t offset) +{ + return ctx->ds->buf[ctx->ds->buf_offset + offset]; +} + +static inline void *get_buf_ptr(struct vrend_decode_ctx *ctx, + uint32_t offset) +{ + return &ctx->ds->buf[ctx->ds->buf_offset + offset]; +} + +static int vrend_decode_create_shader(struct vrend_decode_ctx *ctx, + uint32_t handle, + uint16_t length) +{ + struct pipe_stream_output_info so_info; + uint i; + int ret; + uint32_t shader_offset, req_local_mem = 0; + unsigned num_tokens, num_so_outputs, offlen; + uint8_t *shd_text; + uint32_t type; + + if (length < VIRGL_OBJ_SHADER_HDR_SIZE(0)) + return EINVAL; + + type = get_buf_entry(ctx, VIRGL_OBJ_SHADER_TYPE); + num_tokens = get_buf_entry(ctx, VIRGL_OBJ_SHADER_NUM_TOKENS); + offlen = get_buf_entry(ctx, VIRGL_OBJ_SHADER_OFFSET); + + if (type == PIPE_SHADER_COMPUTE) { + req_local_mem = get_buf_entry(ctx, VIRGL_OBJ_SHADER_SO_NUM_OUTPUTS); + num_so_outputs = 0; + } else { + num_so_outputs = get_buf_entry(ctx, VIRGL_OBJ_SHADER_SO_NUM_OUTPUTS); + if (length < VIRGL_OBJ_SHADER_HDR_SIZE(num_so_outputs)) + return EINVAL; + + if (num_so_outputs > PIPE_MAX_SO_OUTPUTS) + return EINVAL; + } + + shader_offset = 6; + if (num_so_outputs) { + so_info.num_outputs = num_so_outputs; + if (so_info.num_outputs) { + for (i = 0; i < 4; i++) + so_info.stride[i] = get_buf_entry(ctx, VIRGL_OBJ_SHADER_SO_STRIDE(i)); + for (i = 0; i < so_info.num_outputs; i++) { + uint32_t tmp = get_buf_entry(ctx, VIRGL_OBJ_SHADER_SO_OUTPUT0(i)); + + so_info.output[i].register_index = tmp & 0xff; + so_info.output[i].start_component = (tmp >> 8) & 0x3; + so_info.output[i].num_components = (tmp >> 10) & 0x7; + so_info.output[i].output_buffer = (tmp >> 13) & 0x7; + so_info.output[i].dst_offset = (tmp >> 16) & 0xffff; + tmp = get_buf_entry(ctx, VIRGL_OBJ_SHADER_SO_OUTPUT0_SO(i)); + so_info.output[i].stream = (tmp & 0x3); + so_info.output[i].need_temp = so_info.output[i].num_components < 4; + } + + for (i = 0; i < so_info.num_outputs - 1; i++) { + for (unsigned j = i + 1; j < so_info.num_outputs; j++) { + so_info.output[j].need_temp |= + (so_info.output[i].register_index == so_info.output[j].register_index); + } + } + } + shader_offset += 4 + (2 * num_so_outputs); + } else + memset(&so_info, 0, sizeof(so_info)); + + shd_text = get_buf_ptr(ctx, shader_offset); + ret = vrend_create_shader(ctx->grctx, handle, &so_info, req_local_mem, (const char *)shd_text, offlen, num_tokens, type, length - shader_offset + 1); + + return ret; +} + +static int vrend_decode_create_stream_output_target(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + uint32_t res_handle, buffer_size, buffer_offset; + + if (length != VIRGL_OBJ_STREAMOUT_SIZE) + return EINVAL; + + res_handle = get_buf_entry(ctx, VIRGL_OBJ_STREAMOUT_RES_HANDLE); + buffer_offset = get_buf_entry(ctx, VIRGL_OBJ_STREAMOUT_BUFFER_OFFSET); + buffer_size = get_buf_entry(ctx, VIRGL_OBJ_STREAMOUT_BUFFER_SIZE); + + return vrend_create_so_target(ctx->grctx, handle, res_handle, buffer_offset, + buffer_size); +} + +static int vrend_decode_set_framebuffer_state(struct vrend_decode_ctx *ctx, int length) +{ + if (length < 2) + return EINVAL; + + int32_t nr_cbufs = get_buf_entry(ctx, VIRGL_SET_FRAMEBUFFER_STATE_NR_CBUFS); + uint32_t zsurf_handle = get_buf_entry(ctx, VIRGL_SET_FRAMEBUFFER_STATE_NR_ZSURF_HANDLE); + uint32_t surf_handle[8]; + int i; + + if (length != (2 + nr_cbufs)) + return EINVAL; + + if (nr_cbufs > 8) + return EINVAL; + + for (i = 0; i < nr_cbufs; i++) + surf_handle[i] = get_buf_entry(ctx, VIRGL_SET_FRAMEBUFFER_STATE_CBUF_HANDLE(i)); + vrend_set_framebuffer_state(ctx->grctx, nr_cbufs, surf_handle, zsurf_handle); + return 0; +} + +static int vrend_decode_set_framebuffer_state_no_attach(struct vrend_decode_ctx *ctx, int length) +{ + uint32_t width, height; + uint32_t layers, samples; + uint32_t tmp; + + if (length != VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_SIZE) + return EINVAL; + + tmp = get_buf_entry(ctx, VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_WIDTH_HEIGHT); + width = VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_WIDTH(tmp); + height = VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_HEIGHT(tmp); + + tmp = get_buf_entry(ctx, VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_LAYERS_SAMPLES); + layers = VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_LAYERS(tmp); + samples = VIRGL_SET_FRAMEBUFFER_STATE_NO_ATTACH_SAMPLES(tmp); + + vrend_set_framebuffer_state_no_attach(ctx->grctx, width, height, layers, samples); + return 0; +} + +static int vrend_decode_clear(struct vrend_decode_ctx *ctx, int length) +{ + union pipe_color_union color; + double depth; + unsigned stencil, buffers; + int i; + + if (length != VIRGL_OBJ_CLEAR_SIZE) + return EINVAL; + buffers = get_buf_entry(ctx, VIRGL_OBJ_CLEAR_BUFFERS); + for (i = 0; i < 4; i++) + color.ui[i] = get_buf_entry(ctx, VIRGL_OBJ_CLEAR_COLOR_0 + i); + double *depth_ptr = (double *)(uint64_t *)get_buf_ptr(ctx, VIRGL_OBJ_CLEAR_DEPTH_0); + memcpy(&depth, depth_ptr, sizeof(double)); + stencil = get_buf_entry(ctx, VIRGL_OBJ_CLEAR_STENCIL); + + vrend_clear(ctx->grctx, buffers, &color, depth, stencil); + return 0; +} + +static float uif(unsigned int ui) +{ + union { float f; unsigned int ui; } myuif; + myuif.ui = ui; + return myuif.f; +} + +static int vrend_decode_set_viewport_state(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_viewport_state vps[PIPE_MAX_VIEWPORTS]; + uint i, v; + uint32_t num_viewports, start_slot; + if (length < 1) + return EINVAL; + + if ((length - 1) % 6) + return EINVAL; + + num_viewports = (length - 1) / 6; + start_slot = get_buf_entry(ctx, VIRGL_SET_VIEWPORT_START_SLOT); + + if (num_viewports > PIPE_MAX_VIEWPORTS || + start_slot > (PIPE_MAX_VIEWPORTS - num_viewports)) + return EINVAL; + + for (v = 0; v < num_viewports; v++) { + for (i = 0; i < 3; i++) + vps[v].scale[i] = uif(get_buf_entry(ctx, VIRGL_SET_VIEWPORT_STATE_SCALE_0(v) + i)); + for (i = 0; i < 3; i++) + vps[v].translate[i] = uif(get_buf_entry(ctx, VIRGL_SET_VIEWPORT_STATE_TRANSLATE_0(v) + i)); + } + + vrend_set_viewport_states(ctx->grctx, start_slot, num_viewports, vps); + return 0; +} + +static int vrend_decode_set_index_buffer(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1 && length != 3) + return EINVAL; + vrend_set_index_buffer(ctx->grctx, + get_buf_entry(ctx, VIRGL_SET_INDEX_BUFFER_HANDLE), + (length == 3) ? get_buf_entry(ctx, VIRGL_SET_INDEX_BUFFER_INDEX_SIZE) : 0, + (length == 3) ? get_buf_entry(ctx, VIRGL_SET_INDEX_BUFFER_OFFSET) : 0); + return 0; +} + +static int vrend_decode_set_constant_buffer(struct vrend_decode_ctx *ctx, uint16_t length) +{ + uint32_t shader; + uint32_t index; + int nc = (length - 2); + + if (length < 2) + return EINVAL; + + shader = get_buf_entry(ctx, VIRGL_SET_CONSTANT_BUFFER_SHADER_TYPE); + index = get_buf_entry(ctx, VIRGL_SET_CONSTANT_BUFFER_INDEX); + + if (shader >= PIPE_SHADER_TYPES) + return EINVAL; + + vrend_set_constants(ctx->grctx, shader, index, nc, get_buf_ptr(ctx, VIRGL_SET_CONSTANT_BUFFER_DATA_START)); + return 0; +} + +static int vrend_decode_set_uniform_buffer(struct vrend_decode_ctx *ctx, int length) +{ + if (length != VIRGL_SET_UNIFORM_BUFFER_SIZE) + return EINVAL; + + uint32_t shader = get_buf_entry(ctx, VIRGL_SET_UNIFORM_BUFFER_SHADER_TYPE); + uint32_t index = get_buf_entry(ctx, VIRGL_SET_UNIFORM_BUFFER_INDEX); + uint32_t offset = get_buf_entry(ctx, VIRGL_SET_UNIFORM_BUFFER_OFFSET); + uint32_t blength = get_buf_entry(ctx, VIRGL_SET_UNIFORM_BUFFER_LENGTH); + uint32_t handle = get_buf_entry(ctx, VIRGL_SET_UNIFORM_BUFFER_RES_HANDLE); + + if (shader >= PIPE_SHADER_TYPES) + return EINVAL; + + if (index >= PIPE_MAX_CONSTANT_BUFFERS) + return EINVAL; + + vrend_set_uniform_buffer(ctx->grctx, shader, index, offset, blength, handle); + return 0; +} + +static int vrend_decode_set_vertex_buffers(struct vrend_decode_ctx *ctx, uint16_t length) +{ + int num_vbo; + int i; + + /* must be a multiple of 3 */ + if (length && (length % 3)) + return EINVAL; + + num_vbo = (length / 3); + if (num_vbo > PIPE_MAX_ATTRIBS) + return EINVAL; + + for (i = 0; i < num_vbo; i++) { + vrend_set_single_vbo(ctx->grctx, i, + get_buf_entry(ctx, VIRGL_SET_VERTEX_BUFFER_STRIDE(i)), + get_buf_entry(ctx, VIRGL_SET_VERTEX_BUFFER_OFFSET(i)), + get_buf_entry(ctx, VIRGL_SET_VERTEX_BUFFER_HANDLE(i))); + } + vrend_set_num_vbo(ctx->grctx, num_vbo); + return 0; +} + +static int vrend_decode_set_sampler_views(struct vrend_decode_ctx *ctx, uint16_t length) +{ + uint32_t num_samps; + uint32_t i; + uint32_t shader_type; + uint32_t start_slot; + + if (length < 2) + return EINVAL; + num_samps = length - 2; + shader_type = get_buf_entry(ctx, VIRGL_SET_SAMPLER_VIEWS_SHADER_TYPE); + start_slot = get_buf_entry(ctx, VIRGL_SET_SAMPLER_VIEWS_START_SLOT); + + if (shader_type >= PIPE_SHADER_TYPES) + return EINVAL; + + if (num_samps > PIPE_MAX_SHADER_SAMPLER_VIEWS || + start_slot > (PIPE_MAX_SHADER_SAMPLER_VIEWS - num_samps)) + return EINVAL; + + for (i = 0; i < num_samps; i++) { + uint32_t handle = get_buf_entry(ctx, VIRGL_SET_SAMPLER_VIEWS_V0_HANDLE + i); + vrend_set_single_sampler_view(ctx->grctx, shader_type, i + start_slot, handle); + } + vrend_set_num_sampler_views(ctx->grctx, shader_type, start_slot, num_samps); + return 0; +} + +static void vrend_decode_transfer_common(struct vrend_decode_ctx *ctx, struct vrend_transfer_info *info) +{ + info->handle = get_buf_entry(ctx, VIRGL_RESOURCE_IW_RES_HANDLE); + info->level = get_buf_entry(ctx, VIRGL_RESOURCE_IW_LEVEL); + info->stride = get_buf_entry(ctx, VIRGL_RESOURCE_IW_STRIDE); + info->layer_stride = get_buf_entry(ctx, VIRGL_RESOURCE_IW_LAYER_STRIDE); + info->box->x = get_buf_entry(ctx, VIRGL_RESOURCE_IW_X); + info->box->y = get_buf_entry(ctx, VIRGL_RESOURCE_IW_Y); + info->box->z = get_buf_entry(ctx, VIRGL_RESOURCE_IW_Z); + info->box->width = get_buf_entry(ctx, VIRGL_RESOURCE_IW_W); + info->box->height = get_buf_entry(ctx, VIRGL_RESOURCE_IW_H); + info->box->depth = get_buf_entry(ctx, VIRGL_RESOURCE_IW_D); +} + +static int vrend_decode_resource_inline_write(struct vrend_decode_ctx *ctx, uint16_t length) +{ + struct pipe_box box; + struct vrend_transfer_info info; + uint32_t data_len; + struct iovec dataiovec; + void *data; + + if (length < 12) + return EINVAL; + + if (length + ctx->ds->buf_offset > ctx->ds->buf_total) + return EINVAL; + + memset(&info, 0, sizeof(info)); + info.box = &box; + vrend_decode_transfer_common(ctx, &info); + data_len = (length - 11) * 4; + data = get_buf_ptr(ctx, VIRGL_RESOURCE_IW_DATA_START); + + info.ctx_id = 0; + info.offset = 0; + + dataiovec.iov_base = data; + dataiovec.iov_len = data_len; + + info.iovec = &dataiovec; + info.iovec_cnt = 1; + return vrend_transfer_inline_write(ctx->grctx, &info); +} + +static int vrend_decode_draw_vbo(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_draw_info info; + uint32_t cso; + uint32_t handle = 0, indirect_draw_count_handle = 0; + if (length != VIRGL_DRAW_VBO_SIZE && length != VIRGL_DRAW_VBO_SIZE_TESS && + length != VIRGL_DRAW_VBO_SIZE_INDIRECT) + return EINVAL; + memset(&info, 0, sizeof(struct pipe_draw_info)); + + info.start = get_buf_entry(ctx, VIRGL_DRAW_VBO_START); + info.count = get_buf_entry(ctx, VIRGL_DRAW_VBO_COUNT); + info.mode = get_buf_entry(ctx, VIRGL_DRAW_VBO_MODE); + info.indexed = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDEXED); + info.instance_count = get_buf_entry(ctx, VIRGL_DRAW_VBO_INSTANCE_COUNT); + info.index_bias = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDEX_BIAS); + info.start_instance = get_buf_entry(ctx, VIRGL_DRAW_VBO_START_INSTANCE); + info.primitive_restart = get_buf_entry(ctx, VIRGL_DRAW_VBO_PRIMITIVE_RESTART); + info.restart_index = get_buf_entry(ctx, VIRGL_DRAW_VBO_RESTART_INDEX); + info.min_index = get_buf_entry(ctx, VIRGL_DRAW_VBO_MIN_INDEX); + info.max_index = get_buf_entry(ctx, VIRGL_DRAW_VBO_MAX_INDEX); + + if (length >= VIRGL_DRAW_VBO_SIZE_TESS) { + info.vertices_per_patch = get_buf_entry(ctx, VIRGL_DRAW_VBO_VERTICES_PER_PATCH); + info.drawid = get_buf_entry(ctx, VIRGL_DRAW_VBO_DRAWID); + } + + if (length == VIRGL_DRAW_VBO_SIZE_INDIRECT) { + handle = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDIRECT_HANDLE); + info.indirect.offset = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDIRECT_OFFSET); + info.indirect.stride = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDIRECT_STRIDE); + info.indirect.draw_count = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT); + info.indirect.indirect_draw_count_offset = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT_OFFSET); + indirect_draw_count_handle = get_buf_entry(ctx, VIRGL_DRAW_VBO_INDIRECT_DRAW_COUNT_HANDLE); + } + + cso = get_buf_entry(ctx, VIRGL_DRAW_VBO_COUNT_FROM_SO); + + return vrend_draw_vbo(ctx->grctx, &info, cso, handle, indirect_draw_count_handle); +} + +static int vrend_decode_create_blend(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + struct pipe_blend_state *blend_state; + uint32_t tmp; + int i; + + if (length != VIRGL_OBJ_BLEND_SIZE) { + return EINVAL; + } + + blend_state = CALLOC_STRUCT(pipe_blend_state); + if (!blend_state) + return ENOMEM; + + tmp = get_buf_entry(ctx, VIRGL_OBJ_BLEND_S0); + blend_state->independent_blend_enable = (tmp & 1); + blend_state->logicop_enable = (tmp >> 1) & 0x1; + blend_state->dither = (tmp >> 2) & 0x1; + blend_state->alpha_to_coverage = (tmp >> 3) & 0x1; + blend_state->alpha_to_one = (tmp >> 4) & 0x1; + + tmp = get_buf_entry(ctx, VIRGL_OBJ_BLEND_S1); + blend_state->logicop_func = tmp & 0xf; + + for (i = 0; i < PIPE_MAX_COLOR_BUFS; i++) { + tmp = get_buf_entry(ctx, VIRGL_OBJ_BLEND_S2(i)); + blend_state->rt[i].blend_enable = tmp & 0x1; + blend_state->rt[i].rgb_func = (tmp >> 1) & 0x7; + blend_state->rt[i].rgb_src_factor = (tmp >> 4) & 0x1f; + blend_state->rt[i].rgb_dst_factor = (tmp >> 9) & 0x1f; + blend_state->rt[i].alpha_func = (tmp >> 14) & 0x7; + blend_state->rt[i].alpha_src_factor = (tmp >> 17) & 0x1f; + blend_state->rt[i].alpha_dst_factor = (tmp >> 22) & 0x1f; + blend_state->rt[i].colormask = (tmp >> 27) & 0xf; + } + + tmp = vrend_renderer_object_insert(ctx->grctx, blend_state, sizeof(struct pipe_blend_state), handle, + VIRGL_OBJECT_BLEND); + if (tmp == 0) { + FREE(blend_state); + return ENOMEM; + } + return 0; +} + +static int vrend_decode_create_dsa(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + int i; + struct pipe_depth_stencil_alpha_state *dsa_state; + uint32_t tmp; + + if (length != VIRGL_OBJ_DSA_SIZE) + return EINVAL; + + dsa_state = CALLOC_STRUCT(pipe_depth_stencil_alpha_state); + if (!dsa_state) + return ENOMEM; + + tmp = get_buf_entry(ctx, VIRGL_OBJ_DSA_S0); + dsa_state->depth.enabled = tmp & 0x1; + dsa_state->depth.writemask = (tmp >> 1) & 0x1; + dsa_state->depth.func = (tmp >> 2) & 0x7; + + dsa_state->alpha.enabled = (tmp >> 8) & 0x1; + dsa_state->alpha.func = (tmp >> 9) & 0x7; + + for (i = 0; i < 2; i++) { + tmp = get_buf_entry(ctx, VIRGL_OBJ_DSA_S1 + i); + dsa_state->stencil[i].enabled = tmp & 0x1; + dsa_state->stencil[i].func = (tmp >> 1) & 0x7; + dsa_state->stencil[i].fail_op = (tmp >> 4) & 0x7; + dsa_state->stencil[i].zpass_op = (tmp >> 7) & 0x7; + dsa_state->stencil[i].zfail_op = (tmp >> 10) & 0x7; + dsa_state->stencil[i].valuemask = (tmp >> 13) & 0xff; + dsa_state->stencil[i].writemask = (tmp >> 21) & 0xff; + } + + tmp = get_buf_entry(ctx, VIRGL_OBJ_DSA_ALPHA_REF); + dsa_state->alpha.ref_value = uif(tmp); + + tmp = vrend_renderer_object_insert(ctx->grctx, dsa_state, sizeof(struct pipe_depth_stencil_alpha_state), handle, + VIRGL_OBJECT_DSA); + if (tmp == 0) { + FREE(dsa_state); + return ENOMEM; + } + return 0; +} + +static int vrend_decode_create_rasterizer(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + struct pipe_rasterizer_state *rs_state; + uint32_t tmp; + + if (length != VIRGL_OBJ_RS_SIZE) + return EINVAL; + + rs_state = CALLOC_STRUCT(pipe_rasterizer_state); + if (!rs_state) + return ENOMEM; + + tmp = get_buf_entry(ctx, VIRGL_OBJ_RS_S0); +#define ebit(name, bit) rs_state->name = (tmp >> bit) & 0x1 +#define emask(name, bit, mask) rs_state->name = (tmp >> bit) & mask + + ebit(flatshade, 0); + ebit(depth_clip, 1); + ebit(clip_halfz, 2); + ebit(rasterizer_discard, 3); + ebit(flatshade_first, 4); + ebit(light_twoside, 5); + ebit(sprite_coord_mode, 6); + ebit(point_quad_rasterization, 7); + emask(cull_face, 8, 0x3); + emask(fill_front, 10, 0x3); + emask(fill_back, 12, 0x3); + ebit(scissor, 14); + ebit(front_ccw, 15); + ebit(clamp_vertex_color, 16); + ebit(clamp_fragment_color, 17); + ebit(offset_line, 18); + ebit(offset_point, 19); + ebit(offset_tri, 20); + ebit(poly_smooth, 21); + ebit(poly_stipple_enable, 22); + ebit(point_smooth, 23); + ebit(point_size_per_vertex, 24); + ebit(multisample, 25); + ebit(line_smooth, 26); + ebit(line_stipple_enable, 27); + ebit(line_last_pixel, 28); + ebit(half_pixel_center, 29); + ebit(bottom_edge_rule, 30); + ebit(force_persample_interp, 31); + rs_state->point_size = uif(get_buf_entry(ctx, VIRGL_OBJ_RS_POINT_SIZE)); + rs_state->sprite_coord_enable = get_buf_entry(ctx, VIRGL_OBJ_RS_SPRITE_COORD_ENABLE); + tmp = get_buf_entry(ctx, VIRGL_OBJ_RS_S3); + emask(line_stipple_pattern, 0, 0xffff); + emask(line_stipple_factor, 16, 0xff); + emask(clip_plane_enable, 24, 0xff); + + rs_state->line_width = uif(get_buf_entry(ctx, VIRGL_OBJ_RS_LINE_WIDTH)); + rs_state->offset_units = uif(get_buf_entry(ctx, VIRGL_OBJ_RS_OFFSET_UNITS)); + rs_state->offset_scale = uif(get_buf_entry(ctx, VIRGL_OBJ_RS_OFFSET_SCALE)); + rs_state->offset_clamp = uif(get_buf_entry(ctx, VIRGL_OBJ_RS_OFFSET_CLAMP)); + + tmp = vrend_renderer_object_insert(ctx->grctx, rs_state, sizeof(struct pipe_rasterizer_state), handle, + VIRGL_OBJECT_RASTERIZER); + if (tmp == 0) { + FREE(rs_state); + return ENOMEM; + } + return 0; +} + +static int vrend_decode_create_surface(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + uint32_t res_handle, format, val0, val1; + int ret; + + if (length != VIRGL_OBJ_SURFACE_SIZE) + return EINVAL; + + res_handle = get_buf_entry(ctx, VIRGL_OBJ_SURFACE_RES_HANDLE); + format = get_buf_entry(ctx, VIRGL_OBJ_SURFACE_FORMAT); + /* decide later if these are texture or buffer */ + val0 = get_buf_entry(ctx, VIRGL_OBJ_SURFACE_BUFFER_FIRST_ELEMENT); + val1 = get_buf_entry(ctx, VIRGL_OBJ_SURFACE_BUFFER_LAST_ELEMENT); + ret = vrend_create_surface(ctx->grctx, handle, res_handle, format, val0, val1); + return ret; +} + +static int vrend_decode_create_sampler_view(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + uint32_t res_handle, format, val0, val1, swizzle_packed; + + if (length != VIRGL_OBJ_SAMPLER_VIEW_SIZE) + return EINVAL; + + res_handle = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_VIEW_RES_HANDLE); + format = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_VIEW_FORMAT); + val0 = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_VIEW_BUFFER_FIRST_ELEMENT); + val1 = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_VIEW_BUFFER_LAST_ELEMENT); + swizzle_packed = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_VIEW_SWIZZLE); + return vrend_create_sampler_view(ctx->grctx, handle, res_handle, format, val0, val1,swizzle_packed); +} + +static int vrend_decode_create_sampler_state(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + struct pipe_sampler_state state; + int i; + uint32_t tmp; + + if (length != VIRGL_OBJ_SAMPLER_STATE_SIZE) + return EINVAL; + tmp = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_STATE_S0); + state.wrap_s = tmp & 0x7; + state.wrap_t = (tmp >> 3) & 0x7; + state.wrap_r = (tmp >> 6) & 0x7; + state.min_img_filter = (tmp >> 9) & 0x3; + state.min_mip_filter = (tmp >> 11) & 0x3; + state.mag_img_filter = (tmp >> 13) & 0x3; + state.compare_mode = (tmp >> 15) & 0x1; + state.compare_func = (tmp >> 16) & 0x7; + state.seamless_cube_map = (tmp >> 19) & 0x1; + + state.lod_bias = uif(get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_STATE_LOD_BIAS)); + state.min_lod = uif(get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_STATE_MIN_LOD)); + state.max_lod = uif(get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_STATE_MAX_LOD)); + + for (i = 0; i < 4; i++) + state.border_color.ui[i] = get_buf_entry(ctx, VIRGL_OBJ_SAMPLER_STATE_BORDER_COLOR(i)); + + if (state.min_mip_filter != PIPE_TEX_MIPFILTER_NONE && + state.min_mip_filter != PIPE_TEX_MIPFILTER_LINEAR && + state.min_mip_filter != PIPE_TEX_MIPFILTER_NEAREST) + return EINVAL; + + return vrend_create_sampler_state(ctx->grctx, handle, &state); +} + +static int vrend_decode_create_ve(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + struct pipe_vertex_element *ve = NULL; + int num_elements; + int i; + int ret; + + if (length < 1) + return EINVAL; + + if ((length - 1) % 4) + return EINVAL; + + num_elements = (length - 1) / 4; + + if (num_elements) { + ve = calloc(num_elements, sizeof(struct pipe_vertex_element)); + + if (!ve) + return ENOMEM; + + for (i = 0; i < num_elements; i++) { + ve[i].src_offset = get_buf_entry(ctx, VIRGL_OBJ_VERTEX_ELEMENTS_V0_SRC_OFFSET(i)); + ve[i].instance_divisor = get_buf_entry(ctx, VIRGL_OBJ_VERTEX_ELEMENTS_V0_INSTANCE_DIVISOR(i)); + ve[i].vertex_buffer_index = get_buf_entry(ctx, VIRGL_OBJ_VERTEX_ELEMENTS_V0_VERTEX_BUFFER_INDEX(i)); + + if (ve[i].vertex_buffer_index >= PIPE_MAX_ATTRIBS) { + FREE(ve); + return EINVAL; + } + + ve[i].src_format = get_buf_entry(ctx, VIRGL_OBJ_VERTEX_ELEMENTS_V0_SRC_FORMAT(i)); + } + } + + ret = vrend_create_vertex_elements_state(ctx->grctx, handle, num_elements, ve); + + FREE(ve); + return ret; +} + +static int vrend_decode_create_query(struct vrend_decode_ctx *ctx, uint32_t handle, uint16_t length) +{ + uint32_t query_type; + uint32_t query_index; + uint32_t res_handle; + uint32_t offset; + uint32_t tmp; + + if (length != VIRGL_OBJ_QUERY_SIZE) + return EINVAL; + + tmp = get_buf_entry(ctx, VIRGL_OBJ_QUERY_TYPE_INDEX); + query_type = VIRGL_OBJ_QUERY_TYPE(tmp); + query_index = (tmp >> 16) & 0xffff; + + offset = get_buf_entry(ctx, VIRGL_OBJ_QUERY_OFFSET); + res_handle = get_buf_entry(ctx, VIRGL_OBJ_QUERY_RES_HANDLE); + + return vrend_create_query(ctx->grctx, handle, query_type, query_index, res_handle, offset); +} + +static int vrend_decode_create_object(struct vrend_decode_ctx *ctx, int length) +{ + if (length < 1) + return EINVAL; + + uint32_t header = get_buf_entry(ctx, VIRGL_OBJ_CREATE_HEADER); + uint32_t handle = get_buf_entry(ctx, VIRGL_OBJ_CREATE_HANDLE); + uint8_t obj_type = (header >> 8) & 0xff; + int ret = 0; + + if (handle == 0) + return EINVAL; + + switch (obj_type){ + case VIRGL_OBJECT_BLEND: + ret = vrend_decode_create_blend(ctx, handle, length); + break; + case VIRGL_OBJECT_DSA: + ret = vrend_decode_create_dsa(ctx, handle, length); + break; + case VIRGL_OBJECT_RASTERIZER: + ret = vrend_decode_create_rasterizer(ctx, handle, length); + break; + case VIRGL_OBJECT_SHADER: + ret = vrend_decode_create_shader(ctx, handle, length); + break; + case VIRGL_OBJECT_VERTEX_ELEMENTS: + ret = vrend_decode_create_ve(ctx, handle, length); + break; + case VIRGL_OBJECT_SURFACE: + ret = vrend_decode_create_surface(ctx, handle, length); + break; + case VIRGL_OBJECT_SAMPLER_VIEW: + ret = vrend_decode_create_sampler_view(ctx, handle, length); + break; + case VIRGL_OBJECT_SAMPLER_STATE: + ret = vrend_decode_create_sampler_state(ctx, handle, length); + break; + case VIRGL_OBJECT_QUERY: + ret = vrend_decode_create_query(ctx, handle, length); + break; + case VIRGL_OBJECT_STREAMOUT_TARGET: + ret = vrend_decode_create_stream_output_target(ctx, handle, length); + break; + default: + return EINVAL; + } + + return ret; +} + +static int vrend_decode_bind_object(struct vrend_decode_ctx *ctx, uint16_t length) +{ + if (length != 1) + return EINVAL; + + uint32_t header = get_buf_entry(ctx, VIRGL_OBJ_BIND_HEADER); + uint32_t handle = get_buf_entry(ctx, VIRGL_OBJ_BIND_HANDLE); + uint8_t obj_type = (header >> 8) & 0xff; + + switch (obj_type) { + case VIRGL_OBJECT_BLEND: + vrend_object_bind_blend(ctx->grctx, handle); + break; + case VIRGL_OBJECT_DSA: + vrend_object_bind_dsa(ctx->grctx, handle); + break; + case VIRGL_OBJECT_RASTERIZER: + vrend_object_bind_rasterizer(ctx->grctx, handle); + break; + case VIRGL_OBJECT_VERTEX_ELEMENTS: + vrend_bind_vertex_elements_state(ctx->grctx, handle); + break; + default: + return EINVAL; + } + + return 0; +} + +static int vrend_decode_destroy_object(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1) + return EINVAL; + + uint32_t handle = get_buf_entry(ctx, VIRGL_OBJ_DESTROY_HANDLE); + + vrend_renderer_object_destroy(ctx->grctx, handle); + return 0; +} + +static int vrend_decode_set_stencil_ref(struct vrend_decode_ctx *ctx, int length) +{ + if (length != VIRGL_SET_STENCIL_REF_SIZE) + return EINVAL; + + struct pipe_stencil_ref ref; + uint32_t val = get_buf_entry(ctx, VIRGL_SET_STENCIL_REF); + + ref.ref_value[0] = val & 0xff; + ref.ref_value[1] = (val >> 8) & 0xff; + vrend_set_stencil_ref(ctx->grctx, &ref); + return 0; +} + +static int vrend_decode_set_blend_color(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_blend_color color; + int i; + + if (length != VIRGL_SET_BLEND_COLOR_SIZE) + return EINVAL; + + for (i = 0; i < 4; i++) + color.color[i] = uif(get_buf_entry(ctx, VIRGL_SET_BLEND_COLOR(i))); + + vrend_set_blend_color(ctx->grctx, &color); + return 0; +} + +static int vrend_decode_set_scissor_state(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_scissor_state ss[PIPE_MAX_VIEWPORTS]; + uint32_t temp; + int32_t num_scissor; + uint32_t start_slot; + int s; + if (length < 1) + return EINVAL; + + if ((length - 1) % 2) + return EINVAL; + + num_scissor = (length - 1) / 2; + if (num_scissor > PIPE_MAX_VIEWPORTS) + return EINVAL; + + start_slot = get_buf_entry(ctx, VIRGL_SET_SCISSOR_START_SLOT); + + for (s = 0; s < num_scissor; s++) { + temp = get_buf_entry(ctx, VIRGL_SET_SCISSOR_MINX_MINY(s)); + ss[s].minx = temp & 0xffff; + ss[s].miny = (temp >> 16) & 0xffff; + + temp = get_buf_entry(ctx, VIRGL_SET_SCISSOR_MAXX_MAXY(s)); + ss[s].maxx = temp & 0xffff; + ss[s].maxy = (temp >> 16) & 0xffff; + } + + vrend_set_scissor_state(ctx->grctx, start_slot, num_scissor, ss); + return 0; +} + +static int vrend_decode_set_polygon_stipple(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_poly_stipple ps; + int i; + + if (length != VIRGL_POLYGON_STIPPLE_SIZE) + return EINVAL; + + for (i = 0; i < 32; i++) + ps.stipple[i] = get_buf_entry(ctx, VIRGL_POLYGON_STIPPLE_P0 + i); + + vrend_set_polygon_stipple(ctx->grctx, &ps); + return 0; +} + +static int vrend_decode_set_clip_state(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_clip_state clip; + int i, j; + + if (length != VIRGL_SET_CLIP_STATE_SIZE) + return EINVAL; + + for (i = 0; i < 8; i++) + for (j = 0; j < 4; j++) + clip.ucp[i][j] = uif(get_buf_entry(ctx, VIRGL_SET_CLIP_STATE_C0 + (i * 4) + j)); + vrend_set_clip_state(ctx->grctx, &clip); + return 0; +} + +static int vrend_decode_set_sample_mask(struct vrend_decode_ctx *ctx, int length) +{ + unsigned mask; + + if (length != VIRGL_SET_SAMPLE_MASK_SIZE) + return EINVAL; + mask = get_buf_entry(ctx, VIRGL_SET_SAMPLE_MASK_MASK); + vrend_set_sample_mask(ctx->grctx, mask); + return 0; +} + +static int vrend_decode_set_min_samples(struct vrend_decode_ctx *ctx, int length) +{ + unsigned min_samples; + + if (length != VIRGL_SET_MIN_SAMPLES_SIZE) + return EINVAL; + min_samples = get_buf_entry(ctx, VIRGL_SET_MIN_SAMPLES_MASK); + vrend_set_min_samples(ctx->grctx, min_samples); + return 0; +} + +static int vrend_decode_resource_copy_region(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_box box; + uint32_t dst_handle, src_handle; + uint32_t dst_level, dstx, dsty, dstz; + uint32_t src_level; + + if (length != VIRGL_CMD_RESOURCE_COPY_REGION_SIZE) + return EINVAL; + + dst_handle = get_buf_entry(ctx, VIRGL_CMD_RCR_DST_RES_HANDLE); + dst_level = get_buf_entry(ctx, VIRGL_CMD_RCR_DST_LEVEL); + dstx = get_buf_entry(ctx, VIRGL_CMD_RCR_DST_X); + dsty = get_buf_entry(ctx, VIRGL_CMD_RCR_DST_Y); + dstz = get_buf_entry(ctx, VIRGL_CMD_RCR_DST_Z); + src_handle = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_RES_HANDLE); + src_level = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_LEVEL); + box.x = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_X); + box.y = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_Y); + box.z = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_Z); + box.width = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_W); + box.height = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_H); + box.depth = get_buf_entry(ctx, VIRGL_CMD_RCR_SRC_D); + + vrend_renderer_resource_copy_region(ctx->grctx, dst_handle, + dst_level, dstx, dsty, dstz, + src_handle, src_level, + &box); + return 0; +} + + +static int vrend_decode_blit(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_blit_info info; + uint32_t dst_handle, src_handle, temp; + + if (length != VIRGL_CMD_BLIT_SIZE) + return EINVAL; + temp = get_buf_entry(ctx, VIRGL_CMD_BLIT_S0); + info.mask = temp & 0xff; + info.filter = (temp >> 8) & 0x3; + info.scissor_enable = (temp >> 10) & 0x1; + info.render_condition_enable = (temp >> 11) & 0x1; + info.alpha_blend = (temp >> 12) & 0x1; + temp = get_buf_entry(ctx, VIRGL_CMD_BLIT_SCISSOR_MINX_MINY); + info.scissor.minx = temp & 0xffff; + info.scissor.miny = (temp >> 16) & 0xffff; + temp = get_buf_entry(ctx, VIRGL_CMD_BLIT_SCISSOR_MAXX_MAXY); + info.scissor.maxx = temp & 0xffff; + info.scissor.maxy = (temp >> 16) & 0xffff; + dst_handle = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_RES_HANDLE); + info.dst.level = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_LEVEL); + info.dst.format = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_FORMAT); + info.dst.box.x = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_X); + info.dst.box.y = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_Y); + info.dst.box.z = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_Z); + info.dst.box.width = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_W); + info.dst.box.height = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_H); + info.dst.box.depth = get_buf_entry(ctx, VIRGL_CMD_BLIT_DST_D); + + src_handle = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_RES_HANDLE); + info.src.level = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_LEVEL); + info.src.format = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_FORMAT); + info.src.box.x = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_X); + info.src.box.y = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_Y); + info.src.box.z = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_Z); + info.src.box.width = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_W); + info.src.box.height = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_H); + info.src.box.depth = get_buf_entry(ctx, VIRGL_CMD_BLIT_SRC_D); + + vrend_renderer_blit(ctx->grctx, dst_handle, src_handle, &info); + return 0; +} + +static int vrend_decode_bind_sampler_states(struct vrend_decode_ctx *ctx, int length) +{ + if (length < 2) + return EINVAL; + + uint32_t shader_type = get_buf_entry(ctx, VIRGL_BIND_SAMPLER_STATES_SHADER_TYPE); + uint32_t start_slot = get_buf_entry(ctx, VIRGL_BIND_SAMPLER_STATES_START_SLOT); + uint32_t num_states = length - 2; + + if (shader_type >= PIPE_SHADER_TYPES) + return EINVAL; + + vrend_bind_sampler_states(ctx->grctx, shader_type, start_slot, num_states, + get_buf_ptr(ctx, VIRGL_BIND_SAMPLER_STATES_S0_HANDLE)); + return 0; +} + +static int vrend_decode_begin_query(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1) + return EINVAL; + + uint32_t handle = get_buf_entry(ctx, VIRGL_QUERY_BEGIN_HANDLE); + + return vrend_begin_query(ctx->grctx, handle); +} + +static int vrend_decode_end_query(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1) + return EINVAL; + + uint32_t handle = get_buf_entry(ctx, VIRGL_QUERY_END_HANDLE); + + return vrend_end_query(ctx->grctx, handle); +} + +static int vrend_decode_get_query_result(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 2) + return EINVAL; + + uint32_t handle = get_buf_entry(ctx, VIRGL_QUERY_RESULT_HANDLE); + uint32_t wait = get_buf_entry(ctx, VIRGL_QUERY_RESULT_WAIT); + + vrend_get_query_result(ctx->grctx, handle, wait); + return 0; +} + +static int vrend_decode_set_sub_ctx(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1) + return EINVAL; + + uint32_t ctx_sub_id = get_buf_entry(ctx, 1); + + vrend_renderer_set_sub_ctx(ctx->grctx, ctx_sub_id); + return 0; +} + +static int vrend_decode_create_sub_ctx(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1) + return EINVAL; + + uint32_t ctx_sub_id = get_buf_entry(ctx, 1); + + vrend_renderer_create_sub_ctx(ctx->grctx, ctx_sub_id); + return 0; +} + +static int vrend_decode_destroy_sub_ctx(struct vrend_decode_ctx *ctx, int length) +{ + if (length != 1) + return EINVAL; + + uint32_t ctx_sub_id = get_buf_entry(ctx, 1); + + vrend_renderer_destroy_sub_ctx(ctx->grctx, ctx_sub_id); + return 0; +} + +static int vrend_decode_bind_shader(struct vrend_decode_ctx *ctx, int length) +{ + uint32_t handle, type; + if (length != VIRGL_BIND_SHADER_SIZE) + return EINVAL; + + handle = get_buf_entry(ctx, VIRGL_BIND_SHADER_HANDLE); + type = get_buf_entry(ctx, VIRGL_BIND_SHADER_TYPE); + + vrend_bind_shader(ctx->grctx, handle, type); + return 0; +} + +static int vrend_decode_set_tess_state(struct vrend_decode_ctx *ctx, + int length) +{ + float tess_factors[6]; + int i; + + if (length != VIRGL_TESS_STATE_SIZE) + return EINVAL; + + for (i = 0; i < 6; i++) { + tess_factors[i] = uif(get_buf_entry(ctx, i + 1)); + } + vrend_set_tess_state(ctx->grctx, tess_factors); + return 0; +} + +static int vrend_decode_set_shader_buffers(struct vrend_decode_ctx *ctx, uint16_t length) +{ + uint32_t num_ssbo; + uint32_t shader_type, start_slot; + + if (length < 2) + return EINVAL; + + num_ssbo = (length - 2) / VIRGL_SET_SHADER_BUFFER_ELEMENT_SIZE; + shader_type = get_buf_entry(ctx, VIRGL_SET_SHADER_BUFFER_SHADER_TYPE); + start_slot = get_buf_entry(ctx, VIRGL_SET_SHADER_BUFFER_START_SLOT); + if (shader_type >= PIPE_SHADER_TYPES) + return EINVAL; + + if (num_ssbo < 1) + return 0; + + if (start_slot > PIPE_MAX_SHADER_BUFFERS || + start_slot > PIPE_MAX_SHADER_BUFFERS - num_ssbo) + return EINVAL; + + for (uint32_t i = 0; i < num_ssbo; i++) { + uint32_t offset = get_buf_entry(ctx, VIRGL_SET_SHADER_BUFFER_OFFSET(i)); + uint32_t buf_len = get_buf_entry(ctx, VIRGL_SET_SHADER_BUFFER_LENGTH(i)); + uint32_t handle = get_buf_entry(ctx, VIRGL_SET_SHADER_BUFFER_RES_HANDLE(i)); + vrend_set_single_ssbo(ctx->grctx, shader_type, start_slot + i, offset, buf_len, + handle); + } + return 0; +} + +static int vrend_decode_set_atomic_buffers(struct vrend_decode_ctx *ctx, uint16_t length) +{ + uint32_t num_abo; + uint32_t start_slot; + + if (length < 2) + return EINVAL; + + num_abo = (length - 1) / VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE; + start_slot = get_buf_entry(ctx, VIRGL_SET_ATOMIC_BUFFER_START_SLOT); + if (num_abo < 1) + return 0; + + if (start_slot > PIPE_MAX_HW_ATOMIC_BUFFERS || + start_slot > PIPE_MAX_HW_ATOMIC_BUFFERS - num_abo) + return EINVAL; + + for (uint32_t i = 0; i < num_abo; i++) { + uint32_t offset = get_buf_entry(ctx, i * VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE + 2); + uint32_t buf_len = get_buf_entry(ctx, i * VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE + 3); + uint32_t handle = get_buf_entry(ctx, i * VIRGL_SET_ATOMIC_BUFFER_ELEMENT_SIZE + 4); + vrend_set_single_abo(ctx->grctx, start_slot + i, offset, buf_len, handle); + } + + return 0; +} + +static int vrend_decode_set_shader_images(struct vrend_decode_ctx *ctx, uint16_t length) +{ + uint32_t num_images; + uint32_t shader_type, start_slot; + if (length < 2) + return EINVAL; + + num_images = (length - 2) / VIRGL_SET_SHADER_IMAGE_ELEMENT_SIZE; + shader_type = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_SHADER_TYPE); + start_slot = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_START_SLOT); + if (shader_type >= PIPE_SHADER_TYPES) + return EINVAL; + + if (num_images < 1) { + return 0; + } + if (start_slot > PIPE_MAX_SHADER_IMAGES || + start_slot > PIPE_MAX_SHADER_IMAGES - num_images) + return EINVAL; + + for (uint32_t i = 0; i < num_images; i++) { + uint32_t format = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_FORMAT(i)); + uint32_t access = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_ACCESS(i)); + uint32_t layer_offset = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_LAYER_OFFSET(i)); + uint32_t level_size = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_LEVEL_SIZE(i)); + uint32_t handle = get_buf_entry(ctx, VIRGL_SET_SHADER_IMAGE_RES_HANDLE(i)); + vrend_set_single_image_view(ctx->grctx, shader_type, start_slot + i, format, access, + layer_offset, level_size, handle); + } + return 0; +} + +static int vrend_decode_memory_barrier(struct vrend_decode_ctx *ctx, uint16_t length) +{ + if (length != VIRGL_MEMORY_BARRIER_SIZE) + return EINVAL; + + unsigned flags = get_buf_entry(ctx, VIRGL_MEMORY_BARRIER_FLAGS); + vrend_memory_barrier(ctx->grctx, flags); + return 0; +} + +static int vrend_decode_launch_grid(struct vrend_decode_ctx *ctx, uint16_t length) +{ + uint32_t block[3], grid[3]; + uint32_t indirect_handle, indirect_offset; + if (length != VIRGL_LAUNCH_GRID_SIZE) + return EINVAL; + + block[0] = get_buf_entry(ctx, VIRGL_LAUNCH_BLOCK_X); + block[1] = get_buf_entry(ctx, VIRGL_LAUNCH_BLOCK_Y); + block[2] = get_buf_entry(ctx, VIRGL_LAUNCH_BLOCK_Z); + grid[0] = get_buf_entry(ctx, VIRGL_LAUNCH_GRID_X); + grid[1] = get_buf_entry(ctx, VIRGL_LAUNCH_GRID_Y); + grid[2] = get_buf_entry(ctx, VIRGL_LAUNCH_GRID_Z); + indirect_handle = get_buf_entry(ctx, VIRGL_LAUNCH_INDIRECT_HANDLE); + indirect_offset = get_buf_entry(ctx, VIRGL_LAUNCH_INDIRECT_OFFSET); + vrend_launch_grid(ctx->grctx, block, grid, indirect_handle, indirect_offset); + return 0; +} + +static int vrend_decode_set_streamout_targets(struct vrend_decode_ctx *ctx, + uint16_t length) +{ + uint32_t handles[16]; + uint32_t num_handles = length - 1; + uint32_t append_bitmask; + uint i; + + if (length < 1) + return EINVAL; + if (num_handles > ARRAY_SIZE(handles)) + return EINVAL; + + append_bitmask = get_buf_entry(ctx, VIRGL_SET_STREAMOUT_TARGETS_APPEND_BITMASK); + for (i = 0; i < num_handles; i++) + handles[i] = get_buf_entry(ctx, VIRGL_SET_STREAMOUT_TARGETS_H0 + i); + vrend_set_streamout_targets(ctx->grctx, append_bitmask, num_handles, handles); + return 0; +} + +static int vrend_decode_transfer3d(struct virgl_client *client, struct vrend_decode_ctx *ctx, int length, uint32_t ctx_id) +{ + struct pipe_box box; + struct vrend_transfer_info info; + + if (length < VIRGL_TRANSFER3D_SIZE) + return EINVAL; + + memset(&info, 0, sizeof(info)); + info.box = &box; + info.ctx_id = ctx_id; + vrend_decode_transfer_common(ctx, &info); + info.offset = get_buf_entry(ctx, VIRGL_TRANSFER3D_DATA_OFFSET); + int transfer_mode = get_buf_entry(ctx, VIRGL_TRANSFER3D_DIRECTION); + info.context0 = false; + + if (transfer_mode != VIRGL_TRANSFER_TO_HOST && + transfer_mode != VIRGL_TRANSFER_FROM_HOST) + return EINVAL; + + return vrend_renderer_transfer_iov(client, &info, transfer_mode); +} + +static int vrend_decode_copy_transfer3d(struct vrend_decode_ctx *ctx, int length) +{ + struct pipe_box box; + struct vrend_transfer_info info; + uint32_t src_handle; + + if (length != VIRGL_COPY_TRANSFER3D_SIZE) + return EINVAL; + + memset(&info, 0, sizeof(info)); + info.box = &box; + vrend_decode_transfer_common(ctx, &info); + info.offset = get_buf_entry(ctx, VIRGL_COPY_TRANSFER3D_SRC_RES_OFFSET); + info.synchronized = (get_buf_entry(ctx, VIRGL_COPY_TRANSFER3D_SYNCHRONIZED) != 0); + + src_handle = get_buf_entry(ctx, VIRGL_COPY_TRANSFER3D_SRC_RES_HANDLE); + + return vrend_renderer_copy_transfer3d(ctx->grctx, &info, src_handle); +} + +void vrend_renderer_context_create_internal(struct virgl_client *client, uint32_t handle) +{ + struct vrend_decode_ctx *dctx; + + if (handle >= VREND_MAX_CTX) + return; + + dctx = client->dec_ctx[handle]; + if (dctx) + return; + + dctx = malloc(sizeof(struct vrend_decode_ctx)); + if (!dctx) + return; + + dctx->grctx = vrend_create_context(client, handle); + if (!dctx->grctx) { + free(dctx); + return; + } + + dctx->ds = &dctx->ids; + client->dec_ctx[handle] = dctx; +} + +int vrend_renderer_context_create(struct virgl_client *client, uint32_t handle) +{ + if (handle >= VREND_MAX_CTX) + return EINVAL; + + /* context 0 is always available with no guarantees */ + if (handle == 0) + return EINVAL; + + vrend_renderer_context_create_internal(client, handle); + return 0; +} + +void vrend_renderer_context_destroy(struct virgl_client *client, uint32_t handle) +{ + struct vrend_decode_ctx *ctx; + bool ret; + + if (handle >= VREND_MAX_CTX) + return; + + /* never destroy context 0 here, it will be destroyed in vrend_decode_reset()*/ + if (handle == 0) { + return; + } + + ctx = client->dec_ctx[handle]; + if (!ctx) + return; + + client->dec_ctx[handle] = NULL; + ret = vrend_destroy_context(ctx->grctx); + free(ctx); + /* switch to ctx 0 */ + if (ret && handle != 0) + vrend_hw_switch_context(client->dec_ctx[0]->grctx, true); +} + +struct vrend_context *vrend_lookup_renderer_ctx(struct virgl_client *client, uint32_t ctx_id) +{ + if (ctx_id >= VREND_MAX_CTX) + return NULL; + + if (!client->dec_ctx[ctx_id]) + return NULL; + + return client->dec_ctx[ctx_id]->grctx; +} + +int vrend_decode_block(struct virgl_client *client, uint32_t ctx_id, uint32_t *block, int ndw) +{ + struct vrend_decode_ctx *gdctx; + bool bret; + int ret; + if (ctx_id >= VREND_MAX_CTX) + return EINVAL; + + if (client->dec_ctx[ctx_id] == NULL) + return EINVAL; + + gdctx = client->dec_ctx[ctx_id]; + + bret = vrend_hw_switch_context(gdctx->grctx, true); + if (bret == false) + return EINVAL; + + gdctx->ds->buf = block; + gdctx->ds->buf_total = ndw; + gdctx->ds->buf_offset = 0; + + while (gdctx->ds->buf_offset < gdctx->ds->buf_total) { + uint32_t header = gdctx->ds->buf[gdctx->ds->buf_offset]; + uint32_t len = header >> 16; + + ret = 0; + /* check if the guest is doing something bad */ + if (gdctx->ds->buf_offset + len + 1 > gdctx->ds->buf_total) + break; + + switch (header & 0xff) { + case VIRGL_CCMD_CREATE_OBJECT: + ret = vrend_decode_create_object(gdctx, len); + break; + case VIRGL_CCMD_BIND_OBJECT: + ret = vrend_decode_bind_object(gdctx, len); + break; + case VIRGL_CCMD_DESTROY_OBJECT: + ret = vrend_decode_destroy_object(gdctx, len); + break; + case VIRGL_CCMD_CLEAR: + ret = vrend_decode_clear(gdctx, len); + break; + case VIRGL_CCMD_DRAW_VBO: + ret = vrend_decode_draw_vbo(gdctx, len); + break; + case VIRGL_CCMD_SET_FRAMEBUFFER_STATE: + ret = vrend_decode_set_framebuffer_state(gdctx, len); + break; + case VIRGL_CCMD_SET_VERTEX_BUFFERS: + ret = vrend_decode_set_vertex_buffers(gdctx, len); + break; + case VIRGL_CCMD_RESOURCE_INLINE_WRITE: + ret = vrend_decode_resource_inline_write(gdctx, len); + break; + case VIRGL_CCMD_SET_VIEWPORT_STATE: + ret = vrend_decode_set_viewport_state(gdctx, len); + break; + case VIRGL_CCMD_SET_SAMPLER_VIEWS: + ret = vrend_decode_set_sampler_views(gdctx, len); + break; + case VIRGL_CCMD_SET_INDEX_BUFFER: + ret = vrend_decode_set_index_buffer(gdctx, len); + break; + case VIRGL_CCMD_SET_CONSTANT_BUFFER: + ret = vrend_decode_set_constant_buffer(gdctx, len); + break; + case VIRGL_CCMD_SET_STENCIL_REF: + ret = vrend_decode_set_stencil_ref(gdctx, len); + break; + case VIRGL_CCMD_SET_BLEND_COLOR: + ret = vrend_decode_set_blend_color(gdctx, len); + break; + case VIRGL_CCMD_SET_SCISSOR_STATE: + ret = vrend_decode_set_scissor_state(gdctx, len); + break; + case VIRGL_CCMD_BLIT: + ret = vrend_decode_blit(gdctx, len); + break; + case VIRGL_CCMD_RESOURCE_COPY_REGION: + ret = vrend_decode_resource_copy_region(gdctx, len); + break; + case VIRGL_CCMD_BIND_SAMPLER_STATES: + ret = vrend_decode_bind_sampler_states(gdctx, len); + break; + case VIRGL_CCMD_BEGIN_QUERY: + ret = vrend_decode_begin_query(gdctx, len); + break; + case VIRGL_CCMD_END_QUERY: + ret = vrend_decode_end_query(gdctx, len); + break; + case VIRGL_CCMD_GET_QUERY_RESULT: + ret = vrend_decode_get_query_result(gdctx, len); + break; + case VIRGL_CCMD_SET_POLYGON_STIPPLE: + ret = vrend_decode_set_polygon_stipple(gdctx, len); + break; + case VIRGL_CCMD_SET_CLIP_STATE: + ret = vrend_decode_set_clip_state(gdctx, len); + break; + case VIRGL_CCMD_SET_SAMPLE_MASK: + ret = vrend_decode_set_sample_mask(gdctx, len); + break; + case VIRGL_CCMD_SET_MIN_SAMPLES: + ret = vrend_decode_set_min_samples(gdctx, len); + break; + case VIRGL_CCMD_SET_STREAMOUT_TARGETS: + ret = vrend_decode_set_streamout_targets(gdctx, len); + break; + case VIRGL_CCMD_SET_UNIFORM_BUFFER: + ret = vrend_decode_set_uniform_buffer(gdctx, len); + break; + case VIRGL_CCMD_SET_SUB_CTX: + ret = vrend_decode_set_sub_ctx(gdctx, len); + break; + case VIRGL_CCMD_CREATE_SUB_CTX: + ret = vrend_decode_create_sub_ctx(gdctx, len); + break; + case VIRGL_CCMD_DESTROY_SUB_CTX: + ret = vrend_decode_destroy_sub_ctx(gdctx, len); + break; + case VIRGL_CCMD_BIND_SHADER: + ret = vrend_decode_bind_shader(gdctx, len); + break; + case VIRGL_CCMD_SET_TESS_STATE: + ret = vrend_decode_set_tess_state(gdctx, len); + break; + case VIRGL_CCMD_SET_SHADER_BUFFERS: + ret = vrend_decode_set_shader_buffers(gdctx, len); + break; + case VIRGL_CCMD_SET_SHADER_IMAGES: + ret = vrend_decode_set_shader_images(gdctx, len); + break; + case VIRGL_CCMD_SET_ATOMIC_BUFFERS: + ret = vrend_decode_set_atomic_buffers(gdctx, len); + break; + case VIRGL_CCMD_MEMORY_BARRIER: + ret = vrend_decode_memory_barrier(gdctx, len); + break; + case VIRGL_CCMD_LAUNCH_GRID: + ret = vrend_decode_launch_grid(gdctx, len); + break; + case VIRGL_CCMD_SET_FRAMEBUFFER_STATE_NO_ATTACH: + ret = vrend_decode_set_framebuffer_state_no_attach(gdctx, len); + break; + case VIRGL_CCMD_TRANSFER3D: + ret = vrend_decode_transfer3d(client, gdctx, len, ctx_id); + break; + case VIRGL_CCMD_COPY_TRANSFER3D: + ret = vrend_decode_copy_transfer3d(gdctx, len); + break; + case VIRGL_CCMD_END_TRANSFERS: + ret = 0; + break; + case VIRGL_CCMD_SET_TWEAKS: + case VIRGL_CCMD_SET_DEBUG_FLAGS: + break; + default: + ret = EINVAL; + } + + if (ret == EINVAL || ret == ENOMEM) + goto out; + gdctx->ds->buf_offset += (len) + 1; + } + return 0; + out: + return ret; +} + +void vrend_decode_reset(struct virgl_client *client, bool ctx_0_only) +{ + int i; + + vrend_hw_switch_context(client->dec_ctx[0]->grctx, true); + + if (ctx_0_only == false) { + for (i = 1; i < VREND_MAX_CTX; i++) { + if (!client->dec_ctx[i]) + continue; + + if (!client->dec_ctx[i]->grctx) + continue; + + vrend_destroy_context(client->dec_ctx[i]->grctx); + free(client->dec_ctx[i]); + client->dec_ctx[i] = NULL; + } + } else { + vrend_destroy_context(client->dec_ctx[0]->grctx); + free(client->dec_ctx[0]); + client->dec_ctx[0] = NULL; + } +} diff --git a/app/src/main/cpp/virglrenderer/src/vrend_formats.c b/app/src/main/cpp/virglrenderer/src/vrend_formats.c new file mode 100644 index 0000000..5f0ec0b --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_formats.c @@ -0,0 +1,512 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#include "vrend_renderer.h" +#include "vrend_util.h" +#include "util/u_memory.h" +#include "util/u_format.h" + +#define SWIZZLE_INVALID 0xff +#define NO_SWIZZLE { SWIZZLE_INVALID, SWIZZLE_INVALID, SWIZZLE_INVALID, SWIZZLE_INVALID } +#define RRR1_SWIZZLE { PIPE_SWIZZLE_RED, PIPE_SWIZZLE_RED, PIPE_SWIZZLE_RED, PIPE_SWIZZLE_ONE } +#define RGB1_SWIZZLE { PIPE_SWIZZLE_RED, PIPE_SWIZZLE_GREEN, PIPE_SWIZZLE_BLUE, PIPE_SWIZZLE_ONE } +#define ZZZR_SWIZZLE { PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_ZERO, PIPE_SWIZZLE_RED } + +static struct vrend_format_table base_rgba_formats[] = { + { VIRGL_FORMAT_R8G8B8X8_UNORM, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R8G8B8A8_UNORM, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_B8G8R8X8_UNORM, GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE, RGB1_SWIZZLE }, + { VIRGL_FORMAT_B8G8R8A8_UNORM, GL_BGRA_EXT, GL_BGRA_EXT, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + + { VIRGL_FORMAT_A4B4G4R4_UNORM, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NO_SWIZZLE }, + { VIRGL_FORMAT_B5G6R5_UNORM, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NO_SWIZZLE }, + { VIRGL_FORMAT_R16G16B16X16_UNORM, GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R16G16B16A16_UNORM, GL_RGBA16UI, GL_RGBA, GL_UNSIGNED_SHORT, NO_SWIZZLE }, +}; + +static struct vrend_format_table base_depth_formats[] = { + { VIRGL_FORMAT_Z16_UNORM, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, NO_SWIZZLE }, + { VIRGL_FORMAT_Z32_UNORM, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_S8_UINT_Z24_UNORM, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NO_SWIZZLE }, + { VIRGL_FORMAT_Z24X8_UNORM, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_Z32_FLOAT, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, NO_SWIZZLE }, + { VIRGL_FORMAT_Z32_FLOAT_S8X24_UINT, GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, NO_SWIZZLE }, + { VIRGL_FORMAT_X24S8_UINT, GL_STENCIL_INDEX8, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, NO_SWIZZLE }, +}; + +static struct vrend_format_table base_la_formats[] = { + { VIRGL_FORMAT_A8_UNORM, GL_R8, GL_RED, GL_UNSIGNED_BYTE, ZZZR_SWIZZLE }, + { VIRGL_FORMAT_L8_UNORM, GL_R8, GL_RED, GL_UNSIGNED_BYTE, RRR1_SWIZZLE }, + { VIRGL_FORMAT_A16_UNORM, GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT, ZZZR_SWIZZLE }, + { VIRGL_FORMAT_L16_UNORM, GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT, RRR1_SWIZZLE }, +}; + +static struct vrend_format_table rg_base_formats[] = { + { VIRGL_FORMAT_R8_UNORM, GL_R8, GL_RED, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8G8_UNORM, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R16_UNORM, GL_R16_EXT, GL_RED, GL_UNSIGNED_SHORT, NO_SWIZZLE }, + { VIRGL_FORMAT_R16G16_UNORM, GL_R16_EXT, GL_RG, GL_UNSIGNED_SHORT, NO_SWIZZLE }, +}; + +static struct vrend_format_table integer_base_formats[] = { + { VIRGL_FORMAT_R8G8B8A8_UINT, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8G8B8A8_SINT, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, NO_SWIZZLE }, + + { VIRGL_FORMAT_R16G16B16A16_UINT, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, NO_SWIZZLE }, + { VIRGL_FORMAT_R16G16B16A16_SINT, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, NO_SWIZZLE }, + + { VIRGL_FORMAT_R32G32B32A32_UINT, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32G32B32A32_SINT, GL_RGBA32I, GL_RGBA_INTEGER, GL_INT, NO_SWIZZLE }, +}; + +static struct vrend_format_table integer_3comp_formats[] = { + { VIRGL_FORMAT_R8G8B8X8_UINT, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R8G8B8X8_SINT, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R16G16B16X16_UINT, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R16G16B16X16_SINT, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R32G32B32_UINT, GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32G32B32_SINT, GL_RGB32I, GL_RGB_INTEGER, GL_INT, NO_SWIZZLE }, +}; + +static struct vrend_format_table float_base_formats[] = { + { VIRGL_FORMAT_R16G16B16A16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32G32B32A32_FLOAT, GL_RGBA32F, GL_RGBA, GL_FLOAT, NO_SWIZZLE }, +}; + +static struct vrend_format_table float_la_formats[] = { + { VIRGL_FORMAT_L16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT, RRR1_SWIZZLE }, + { VIRGL_FORMAT_L32_FLOAT, GL_R32F, GL_RED, GL_FLOAT, RRR1_SWIZZLE }, +}; + +static struct vrend_format_table integer_rg_formats[] = { + { VIRGL_FORMAT_R8_UINT, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8G8_UINT, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8_SINT, GL_R8I, GL_RED_INTEGER, GL_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8G8_SINT, GL_RG8I, GL_RG_INTEGER, GL_BYTE, NO_SWIZZLE }, + + { VIRGL_FORMAT_R16_UINT, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, NO_SWIZZLE }, + { VIRGL_FORMAT_R16G16_UINT, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, NO_SWIZZLE }, + { VIRGL_FORMAT_R16_SINT, GL_R16I, GL_RED_INTEGER, GL_SHORT, NO_SWIZZLE }, + { VIRGL_FORMAT_R16G16_SINT, GL_RG16I, GL_RG_INTEGER, GL_SHORT, NO_SWIZZLE }, + + { VIRGL_FORMAT_R32_UINT, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32G32_UINT, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32_SINT, GL_R32I, GL_RED_INTEGER, GL_INT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32G32_SINT, GL_RG32I, GL_RG_INTEGER, GL_INT, NO_SWIZZLE }, +}; + +static struct vrend_format_table float_rg_formats[] = { + { VIRGL_FORMAT_R16_FLOAT, GL_R16F, GL_RED, GL_HALF_FLOAT, NO_SWIZZLE }, + { VIRGL_FORMAT_R16G16_FLOAT, GL_RG16F, GL_RG, GL_HALF_FLOAT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32_FLOAT, GL_R32F, GL_RED, GL_FLOAT, NO_SWIZZLE }, + { VIRGL_FORMAT_R32G32_FLOAT, GL_RG32F, GL_RG, GL_FLOAT, NO_SWIZZLE }, +}; + +static struct vrend_format_table float_3comp_formats[] = { + { VIRGL_FORMAT_R16G16B16X16_FLOAT, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R32G32B32_FLOAT, GL_RGB32F, GL_RGB, GL_FLOAT, NO_SWIZZLE }, +}; + +static struct vrend_format_table integer_la_formats[] = { + { VIRGL_FORMAT_L8_UINT, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, RRR1_SWIZZLE }, + { VIRGL_FORMAT_L8_SINT, GL_R8I, GL_RED_INTEGER, GL_BYTE, RRR1_SWIZZLE }, + + { VIRGL_FORMAT_L16_UINT, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, RRR1_SWIZZLE }, + + { VIRGL_FORMAT_L16_SINT, GL_R16I, GL_RED_INTEGER, GL_SHORT, RRR1_SWIZZLE }, + + { VIRGL_FORMAT_L32_UINT, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, RRR1_SWIZZLE }, + + { VIRGL_FORMAT_L32_SINT, GL_R32I, GL_RED_INTEGER, GL_INT, RRR1_SWIZZLE }, +}; + +static struct vrend_format_table snorm_formats[] = { + { VIRGL_FORMAT_R8_SNORM, GL_R8_SNORM, GL_RED, GL_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8G8_SNORM, GL_RG8_SNORM, GL_RG, GL_BYTE, NO_SWIZZLE }, + + { VIRGL_FORMAT_R8G8B8A8_SNORM, GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, NO_SWIZZLE }, + { VIRGL_FORMAT_R8G8B8X8_SNORM, GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, RGB1_SWIZZLE }, +}; + +static struct vrend_format_table snorm_la_formats[] = { + { VIRGL_FORMAT_A8_SNORM, GL_R8_SNORM, GL_ALPHA, GL_BYTE, ZZZR_SWIZZLE }, + { VIRGL_FORMAT_L8_SNORM, GL_R8_SNORM, GL_RED, GL_BYTE, RRR1_SWIZZLE }, +}; + +static struct vrend_format_table srgb_formats[] = { + { VIRGL_FORMAT_R8G8B8X8_SRGB, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R8G8B8A8_SRGB, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, NO_SWIZZLE }, + + { VIRGL_FORMAT_L8_SRGB, GL_SR8_EXT, GL_RED, GL_UNSIGNED_BYTE, RRR1_SWIZZLE }, + { VIRGL_FORMAT_R8_SRGB, GL_SR8_EXT, GL_RED, GL_UNSIGNED_BYTE, NO_SWIZZLE }, +}; + +static struct vrend_format_table bit10_formats[] = { + { VIRGL_FORMAT_B10G10R10X2_UNORM, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, RGB1_SWIZZLE }, + { VIRGL_FORMAT_B10G10R10A2_UNORM, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, NO_SWIZZLE }, + { VIRGL_FORMAT_R10G10B10X2_UNORM, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, RGB1_SWIZZLE }, + { VIRGL_FORMAT_R10G10B10A2_UNORM, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, NO_SWIZZLE }, + { VIRGL_FORMAT_R10G10B10A2_UINT, GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV, NO_SWIZZLE }, +}; + +static struct vrend_format_table packed_float_formats[] = { + { VIRGL_FORMAT_R11G11B10_FLOAT, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, NO_SWIZZLE }, +}; + +static struct vrend_format_table exponent_float_formats[] = { + { VIRGL_FORMAT_R9G9B9E5_FLOAT, GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV, NO_SWIZZLE }, +}; + +static bool color_format_can_readback(struct vrend_format_table *virgl_format, int gles_ver) +{ + GLint imp = 0; + + if (virgl_format->format == VIRGL_FORMAT_R8G8B8A8_UNORM) + return true; + + if (gles_ver >= 30 && + (virgl_format->format == VIRGL_FORMAT_R32G32B32A32_SINT || + virgl_format->format == VIRGL_FORMAT_R32G32B32A32_UINT)) + return true; + + if ((virgl_format->format == VIRGL_FORMAT_R32G32B32A32_FLOAT) && + (gles_ver >= 32 || vrend_has_gl_extension("GL_EXT_color_buffer_float"))) + return true; + + /* Hotfix for the CI, on GLES these formats are defined like + * VIRGL_FORMAT_R10G10B10.2_UNORM, and seems to be incorrect for direct + * readback but the blit workaround seems to work, so disable the + * direct readback for these two formats. */ + if (virgl_format->format == VIRGL_FORMAT_B10G10R10A2_UNORM || + virgl_format->format == VIRGL_FORMAT_B10G10R10X2_UNORM) + return false; + + /* Check implementation specific readback formats */ + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &imp); + if (imp == (GLint)virgl_format->gltype) { + glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &imp); + if (imp == (GLint)virgl_format->glformat) + return true; + } + return false; +} + +static void vrend_add_formats(struct vrend_format_table *table, int num_entries) +{ + int i; + const int gles_ver = vrend_gl_version(); + + for (i = 0; i < num_entries; i++) { + GLenum status; + bool is_depth = false; + uint32_t flags = 0; + uint32_t binding = 0; + GLuint buffers; + GLuint tex_id, fb_id; + + glGenTextures(1, &tex_id); + glGenFramebuffers(1, &fb_id); + + glBindTexture(GL_TEXTURE_2D, tex_id); + glBindFramebuffer(GL_FRAMEBUFFER, fb_id); + + glTexImage2D(GL_TEXTURE_2D, 0, table[i].internalformat, 32, 32, 0, table[i].glformat, table[i].gltype, NULL); + status = glGetError(); + if (status != GL_NO_ERROR) { + glDeleteTextures(1, &tex_id); + glDeleteFramebuffers(1, &fb_id); + continue; + } + + if (table[i].format < VIRGL_FORMAT_MAX && util_format_is_depth_or_stencil(table[i].format)) { + GLenum attachment; + + if (table[i].format == VIRGL_FORMAT_Z24X8_UNORM || table[i].format == VIRGL_FORMAT_Z32_UNORM || table[i].format == VIRGL_FORMAT_Z16_UNORM || table[i].format == VIRGL_FORMAT_Z32_FLOAT) + attachment = GL_DEPTH_ATTACHMENT; + else + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex_id, 0); + + is_depth = true; + + buffers = GL_NONE; + glDrawBuffers(1, &buffers); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id, 0); + + buffers = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &buffers); + } + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + binding = VIRGL_BIND_SAMPLER_VIEW; + if (status == GL_FRAMEBUFFER_COMPLETE) { + binding |= is_depth ? VIRGL_BIND_DEPTH_STENCIL : VIRGL_BIND_RENDER_TARGET; + + if (color_format_can_readback(&table[i], gles_ver)) + flags |= VIRGL_TEXTURE_CAN_READBACK; + } + + glDeleteTextures(1, &tex_id); + glDeleteFramebuffers(1, &fb_id); + + if (table[i].swizzle[0] != SWIZZLE_INVALID) + vrend_insert_format_swizzle(table[i].format, &table[i], binding, table[i].swizzle, flags); + else + vrend_insert_format(&table[i], binding, flags); + } +} + +#define add_formats(x) vrend_add_formats((x), ARRAY_SIZE((x))) + +void vrend_build_format_list(void) +{ + add_formats(base_rgba_formats); + add_formats(base_depth_formats); + add_formats(base_la_formats); + + /* float support */ + add_formats(float_base_formats); + add_formats(float_la_formats); + add_formats(float_3comp_formats); + + /* texture integer support ? */ + add_formats(integer_base_formats); + add_formats(integer_la_formats); + add_formats(integer_3comp_formats); + + /* RG support? */ + add_formats(rg_base_formats); + /* integer + rg */ + add_formats(integer_rg_formats); + /* float + rg */ + add_formats(float_rg_formats); + + /* snorm */ + add_formats(snorm_formats); + add_formats(snorm_la_formats); + + add_formats(srgb_formats); + + add_formats(bit10_formats); + + add_formats(packed_float_formats); + add_formats(exponent_float_formats); +} + +/* glTexStorage may not support all that is supported by glTexImage, + * so add a flag to indicate when it can be used. + */ +void vrend_check_texture_storage(struct vrend_format_table *table) +{ + int i; + GLuint tex_id; + for (i = 0; i < VIRGL_FORMAT_MAX_EXTENDED; i++) { + + if (table[i].internalformat != 0 && + !(table[i].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE)) { + glGenTextures(1, &tex_id); + glBindTexture(GL_TEXTURE_2D, tex_id); + glTexStorage2D(GL_TEXTURE_2D, 1, table[i].internalformat, 32, 32); + if (glGetError() == GL_NO_ERROR) + table[i].flags |= VIRGL_TEXTURE_CAN_TEXTURE_STORAGE; + glDeleteTextures(1, &tex_id); + } + } +} + +bool vrend_check_framebuffer_mixed_color_attachements() +{ + GLuint tex_id[2]; + GLuint fb_id; + bool retval = false; + + glGenTextures(2, tex_id); + glGenFramebuffers(1, &fb_id); + + glBindTexture(GL_TEXTURE_2D, tex_id[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glBindFramebuffer(GL_FRAMEBUFFER, fb_id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_id[0], 0); + + glBindTexture(GL_TEXTURE_2D, tex_id[1]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 32, 32, 0, GL_RED, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, tex_id[1], 0); + + retval = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + + glDeleteFramebuffers(1, &fb_id); + glDeleteTextures(2, tex_id); + + return retval; +} + +unsigned vrend_renderer_query_multisample_caps(unsigned max_samples, struct virgl_caps_v2 *caps) +{ + GLuint tex; + GLuint fbo; + GLenum status; + + uint max_samples_confirmed = 1; + uint test_num_samples[4] = {2,4,8,16}; + int out_buf_offsets[4] = {0,1,2,4}; + int lowest_working_ms_count_idx = -1; + + assert(glGetError() == GL_NO_ERROR && + "Stale error state detected, please check for failures in initialization"); + + glGenFramebuffers( 1, &fbo ); + memset(caps->sample_locations, 0, 8 * sizeof(uint32_t)); + + for (int i = 3; i >= 0; i--) { + if (test_num_samples[i] > max_samples) + continue; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex); + glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, test_num_samples[i], GL_RGBA32F, 64, 64, GL_TRUE); + status = glGetError(); + if (status == GL_NO_ERROR) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0); + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status == GL_FRAMEBUFFER_COMPLETE) { + if (max_samples_confirmed < test_num_samples[i]) + max_samples_confirmed = test_num_samples[i]; + + for (uint k = 0; k < test_num_samples[i]; ++k) { + float msp[2]; + uint32_t compressed; + glGetMultisamplefv(GL_SAMPLE_POSITION, k, msp); + compressed = ((unsigned)(floor(msp[0] * 16.0f)) & 0xf) << 4; + compressed |= ((unsigned)(floor(msp[1] * 16.0f)) & 0xf); + caps->sample_locations[out_buf_offsets[i] + (k >> 2)] |= compressed << (8 * (k & 3)); + } + lowest_working_ms_count_idx = i; + } else { + /* If a framebuffer doesn't support low sample counts, + * use the sample position from the last working larger count. */ + if (lowest_working_ms_count_idx > 0) { + for (uint k = 0; k < test_num_samples[i]; ++k) { + caps->sample_locations[out_buf_offsets[i] + (k >> 2)] = + caps->sample_locations[out_buf_offsets[lowest_working_ms_count_idx] + (k >> 2)]; + } + } + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + glDeleteTextures(1, &tex); + } + glDeleteFramebuffers(1, &fbo); + return max_samples_confirmed; +} + +/* returns: 1 = compatible, -1 = not compatible, 0 = undecided */ +static int format_uncompressed_compressed_copy_compatible(enum virgl_formats src, + enum virgl_formats dst) +{ + switch (src) { + case VIRGL_FORMAT_R32G32B32A32_UINT: + case VIRGL_FORMAT_R32G32B32A32_SINT: + case VIRGL_FORMAT_R32G32B32A32_FLOAT: + case VIRGL_FORMAT_R32G32B32A32_SNORM: + case VIRGL_FORMAT_R32G32B32A32_UNORM: + switch (dst) { + case VIRGL_FORMAT_DXT3_RGBA: + case VIRGL_FORMAT_DXT3_SRGBA: + case VIRGL_FORMAT_DXT5_RGBA: + case VIRGL_FORMAT_DXT5_SRGBA: + case VIRGL_FORMAT_RGTC2_UNORM: + case VIRGL_FORMAT_RGTC2_SNORM: + case VIRGL_FORMAT_BPTC_RGBA_UNORM: + case VIRGL_FORMAT_BPTC_SRGBA: + case VIRGL_FORMAT_BPTC_RGB_FLOAT: + case VIRGL_FORMAT_BPTC_RGB_UFLOAT: + return 1; + default: + return -1; + } + case VIRGL_FORMAT_R16G16B16A16_UINT: + case VIRGL_FORMAT_R16G16B16A16_SINT: + case VIRGL_FORMAT_R16G16B16A16_FLOAT: + case VIRGL_FORMAT_R16G16B16A16_SNORM: + case VIRGL_FORMAT_R16G16B16A16_UNORM: + case VIRGL_FORMAT_R32G32_UINT: + case VIRGL_FORMAT_R32G32_SINT: + case VIRGL_FORMAT_R32G32_FLOAT: + case VIRGL_FORMAT_R32G32_UNORM: + case VIRGL_FORMAT_R32G32_SNORM: + switch (dst) { + case VIRGL_FORMAT_DXT1_RGBA: + case VIRGL_FORMAT_DXT1_SRGBA: + case VIRGL_FORMAT_DXT1_RGB: + case VIRGL_FORMAT_DXT1_SRGB: + case VIRGL_FORMAT_RGTC1_UNORM: + case VIRGL_FORMAT_RGTC1_SNORM: + return 1; + default: + return -1; + } + default: + return 0; + } +} + +static boolean format_compressed_compressed_copy_compatible(enum virgl_formats src, enum virgl_formats dst) +{ + if ((src == VIRGL_FORMAT_RGTC1_UNORM && dst == VIRGL_FORMAT_RGTC1_SNORM) || + (src == VIRGL_FORMAT_RGTC2_UNORM && dst == VIRGL_FORMAT_RGTC2_SNORM) || + (src == VIRGL_FORMAT_BPTC_RGBA_UNORM && dst == VIRGL_FORMAT_BPTC_SRGBA) || + (src == VIRGL_FORMAT_BPTC_RGB_FLOAT && dst == VIRGL_FORMAT_BPTC_RGB_UFLOAT)) + return true; + return false; +} + +boolean format_is_copy_compatible(enum virgl_formats src, enum virgl_formats dst, + boolean allow_compressed) +{ + int r; + + if (src == dst) + return true; + + if (util_format_is_plain(src) && util_format_is_plain(dst)) { + const struct util_format_description *src_desc = util_format_description(src); + const struct util_format_description *dst_desc = util_format_description(dst); + return util_is_format_compatible(src_desc, dst_desc); + } + + if (!allow_compressed) + return false; + + /* compressed-uncompressed */ + r = format_uncompressed_compressed_copy_compatible(src, dst); + if (r) + return r > 0; + + r = format_uncompressed_compressed_copy_compatible(dst, src); + if (r) + return r > 0; + + return format_compressed_compressed_copy_compatible(dst, src); +} diff --git a/app/src/main/cpp/virglrenderer/src/vrend_iov.h b/app/src/main/cpp/virglrenderer/src/vrend_iov.h new file mode 100644 index 0000000..89028cd --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_iov.h @@ -0,0 +1,60 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#ifndef VREND_IOV_H +#define VREND_IOV_H + +#include +#include +#include + +struct vrend_transfer_info { + uint32_t handle; + uint32_t ctx_id; + int level; + uint32_t stride; + uint32_t layer_stride; + unsigned int iovec_cnt; + struct iovec *iovec; + uint64_t offset; + bool context0; + struct pipe_box *box; + bool synchronized; +}; + +typedef void (*iov_cb)(void *cookie, unsigned int doff, void *src, int len); + +size_t vrend_get_iovec_size(const struct iovec *iov, int iovlen); +size_t vrend_read_from_iovec(const struct iovec *iov, int iov_cnt, + size_t offset, char *buf, size_t bytes); +size_t vrend_write_to_iovec(const struct iovec *iov, int iov_cnt, + size_t offset, const char *buf, size_t bytes); + +size_t vrend_read_from_iovec_cb(const struct iovec *iov, int iov_cnt, + size_t offset, size_t bytes, iov_cb iocb, void *cookie); + +int vrend_copy_iovec(const struct iovec *src_iov, int src_iovlen, size_t src_offset, + const struct iovec *dst_iov, int dst_iovlen, size_t dst_offset, + size_t count, char *buf); + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/vrend_object.c b/app/src/main/cpp/virglrenderer/src/vrend_object.c new file mode 100644 index 0000000..c8cdf60 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_object.c @@ -0,0 +1,198 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "util/u_pointer.h" +#include "util/u_memory.h" +#include "util/u_hash_table.h" + +#include "vrend_object.h" + +struct vrend_object_types { + void (*unref)(void *); +} obj_types[VIRGL_MAX_OBJECTS]; + +static void (*resource_unref)(void *); + +void vrend_object_set_destroy_callback(int type, void (*cb)(void *)) +{ + obj_types[type].unref = cb; +} + +void vrend_resource_set_destroy_callback(void (*cb)(void *)) +{ + resource_unref = cb; +} + +static unsigned +hash_func(void *key) +{ + intptr_t ip = pointer_to_intptr(key); + return (unsigned)(ip & 0xffffffff); +} + +static int +compare(void *key1, void *key2) +{ + if (key1 < key2) + return -1; + if (key1 > key2) + return 1; + else + return 0; +} + +struct vrend_object { + enum virgl_object_type type; + uint32_t handle; + void *data; + bool free_data; +}; + +static void free_object(void *value) +{ + struct vrend_object *obj = value; + + if (obj->free_data) { + if (obj_types[obj->type].unref) + obj_types[obj->type].unref(obj->data); + else { + /* for objects with no callback just free them */ + free(obj->data); + } + } + free(obj); +} + +struct util_hash_table *vrend_object_init_ctx_table(void) +{ + struct util_hash_table *ctx_hash; + ctx_hash = util_hash_table_create(hash_func, compare, free_object); + return ctx_hash; +} + +void vrend_object_fini_ctx_table(struct util_hash_table *ctx_hash) +{ + if (!ctx_hash) + return; + + util_hash_table_destroy(ctx_hash); +} + +static void free_res(void *value) +{ + struct vrend_object *obj = value; + (*resource_unref)(obj->data); + free(obj); +} + +void vrend_object_init_resource_table(struct virgl_client *client) +{ + if (!client->res_hash) + client->res_hash = util_hash_table_create(hash_func, compare, free_res); +} + +void vrend_object_fini_resource_table(struct virgl_client *client) +{ + if (client->res_hash) + util_hash_table_destroy(client->res_hash); + + client->res_hash = NULL; +} + +uint32_t vrend_object_insert_nofree(struct util_hash_table *handle_hash, + void *data, UNUSED uint32_t length, uint32_t handle, + enum virgl_object_type type, bool free_data) +{ + struct vrend_object *obj = CALLOC_STRUCT(vrend_object); + + if (!obj) + return 0; + obj->handle = handle; + obj->data = data; + obj->type = type; + obj->free_data = free_data; + util_hash_table_set(handle_hash, intptr_to_pointer(obj->handle), obj); + return obj->handle; +} + +uint32_t +vrend_object_insert(struct util_hash_table *handle_hash, + void *data, uint32_t length, uint32_t handle, enum virgl_object_type type) +{ + return vrend_object_insert_nofree(handle_hash, data, length, + handle, type, true); +} + +void +vrend_object_remove(struct util_hash_table *handle_hash, + uint32_t handle, UNUSED enum virgl_object_type type) +{ + util_hash_table_remove(handle_hash, intptr_to_pointer(handle)); +} + +void *vrend_object_lookup(struct util_hash_table *handle_hash, + uint32_t handle, enum virgl_object_type type) +{ + struct vrend_object *obj; + + obj = util_hash_table_get(handle_hash, intptr_to_pointer(handle)); + if (!obj) { + return NULL; + } + + if (obj->type != type) + return NULL; + return obj->data; +} + +int vrend_resource_insert(struct virgl_client *client, void *data, uint32_t handle) +{ + struct vrend_object *obj; + + if (!handle) + return 0; + + obj = CALLOC_STRUCT(vrend_object); + if (!obj) + return 0; + + obj->handle = handle; + obj->data = data; + util_hash_table_set(client->res_hash, intptr_to_pointer(obj->handle), obj); + return obj->handle; +} + +void vrend_resource_remove(struct virgl_client *client, uint32_t handle) +{ + util_hash_table_remove(client->res_hash, intptr_to_pointer(handle)); +} + +void *vrend_resource_lookup(struct virgl_client *client, uint32_t handle, UNUSED uint32_t ctx_id) +{ + struct vrend_object *obj; + obj = util_hash_table_get(client->res_hash, intptr_to_pointer(handle)); + if (!obj) + return NULL; + return obj->data; +} diff --git a/app/src/main/cpp/virglrenderer/src/vrend_object.h b/app/src/main/cpp/virglrenderer/src/vrend_object.h new file mode 100644 index 0000000..b298908 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_object.h @@ -0,0 +1,53 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef VREND_OBJECT_H +#define VREND_OBJECT_H + +#include "virgl_protocol.h" +#include "virgl_server.h" + +void vrend_object_init_resource_table(struct virgl_client *client); +void vrend_object_fini_resource_table(struct virgl_client *client); + +struct util_hash_table *vrend_object_init_ctx_table(void); +void vrend_object_fini_ctx_table(struct util_hash_table *ctx_hash); + +void vrend_object_remove(struct util_hash_table *handle_hash, uint32_t handle, enum virgl_object_type obj); +void *vrend_object_lookup(struct util_hash_table *handle_hash, uint32_t handle, enum virgl_object_type obj); +uint32_t vrend_object_insert(struct util_hash_table *handle_hash, void *data, uint32_t length, uint32_t handle, enum virgl_object_type type); +uint32_t vrend_object_insert_nofree(struct util_hash_table *handle_hash, + void *data, uint32_t length, + uint32_t handle, + enum virgl_object_type type, + bool free_data); +/* resources are global */ +int vrend_resource_insert(struct virgl_client *client, void *data, uint32_t handle); + +void vrend_resource_remove(struct virgl_client *client, uint32_t handle); +void *vrend_resource_lookup(struct virgl_client *client, uint32_t handle, uint32_t ctx_id); + +void vrend_object_set_destroy_callback(int type, void (*cb)(void *)); +void vrend_resource_set_destroy_callback(void (*cb)(void *)); +#endif diff --git a/app/src/main/cpp/virglrenderer/src/vrend_renderer.c b/app/src/main/cpp/virglrenderer/src/vrend_renderer.c new file mode 100644 index 0000000..6826872 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_renderer.c @@ -0,0 +1,7366 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include +#include +#include +#include "pipe/p_shader_tokens.h" + +#include "pipe/p_context.h" +#include "pipe/p_defines.h" +#include "pipe/p_screen.h" +#include "pipe/p_state.h" +#include "util/u_inlines.h" +#include "util/u_memory.h" +#include "util/u_dual_blend.h" + +#include "util/u_format.h" +#include "tgsi/tgsi_parse.h" + +#include "vrend_object.h" +#include "vrend_shader.h" + +#include "vrend_renderer.h" + +#include "vrend_util.h" + +#include "virgl_hw.h" + +#include "tgsi/tgsi_text.h" + +#include + +static const uint32_t fake_occlusion_query_samples_passed_default = 1024; + +struct vrend_if_cbs *vrend_clicbs; + +struct vrend_fence { + uint32_t fence_id; + uint32_t ctx_id; + GLsync syncobj; + struct list_head fences; +}; + +struct vrend_query { + struct list_head waiting_queries; + + GLuint id; + GLuint type; + GLuint index; + GLuint gltype; + int ctx_id; + struct vrend_resource *res; + bool fake_samples_passed; +}; + +enum features_id +{ + feat_arb_or_gles_ext_texture_buffer, + feat_atomic_counters, + feat_barrier, + feat_clip_control, + feat_compute_shader, + feat_copy_image, + feat_cube_map_array, + feat_draw_instance, + feat_fb_no_attach, + feat_framebuffer_fetch, + feat_geometry_shader, + feat_gles31_compatibility, + feat_gles31_vertex_attrib_binding, + feat_gpu_shader5, + feat_images, + feat_indep_blend, + feat_indep_blend_func, + feat_indirect_draw, + feat_multisample, + feat_occlusion_query_boolean, + feat_robust_buffer_access, + feat_sample_mask, + feat_sample_shading, + feat_samplers, + feat_separate_shader_objects, + feat_ssbo, + feat_ssbo_barrier, + feat_srgb_write_control, + feat_stencil_texturing, + feat_storage_multisample, + feat_tessellation, + feat_texture_array, + feat_texture_buffer_range, + feat_texture_gather, + feat_texture_multisample, + feat_texture_srgb_decode, + feat_texture_storage, + feat_transform_feedback, + feat_transform_feedback2, + feat_ubo, + feat_last, +}; + +#define FEAT_MAX_EXTS 4 +#define UNAVAIL INT_MAX + +#define FEAT(NAME, GLVER, GLESVER, ...) \ + [feat_ ## NAME ] = {GLVER, GLESVER, { __VA_ARGS__ }, #NAME} + +static const struct { + int gl_ver; + int gles_ver; + const char *gl_ext[FEAT_MAX_EXTS]; + const char *log_name; +} feature_list[] = { + FEAT(arb_or_gles_ext_texture_buffer, 31, UNAVAIL, "GL_ARB_texture_buffer_object", "GL_EXT_texture_buffer", NULL), + FEAT(atomic_counters, 42, 31, "GL_ARB_shader_atomic_counters" ), + FEAT(barrier, 42, 31, NULL), + FEAT(clip_control, 45, UNAVAIL, "GL_ARB_clip_control", "GL_EXT_clip_control"), + FEAT(compute_shader, 43, 31, "GL_ARB_compute_shader" ), + FEAT(copy_image, 43, 32, "GL_ARB_copy_image", "GL_EXT_copy_image", "GL_OES_copy_image" ), + FEAT(cube_map_array, 40, 32, "GL_ARB_texture_cube_map_array", "GL_EXT_texture_cube_map_array", "GL_OES_texture_cube_map_array" ), + FEAT(draw_instance, 31, 30, "GL_ARB_draw_instanced" ), + FEAT(fb_no_attach, 43, 31, "GL_ARB_framebuffer_no_attachments" ), + FEAT(framebuffer_fetch, UNAVAIL, UNAVAIL, "GL_EXT_shader_framebuffer_fetch" ), + FEAT(geometry_shader, 32, 32, "GL_EXT_geometry_shader", "GL_OES_geometry_shader"), + FEAT(gles31_compatibility, 45, 31, "ARB_ES3_1_compatibility" ), + FEAT(gles31_vertex_attrib_binding, 43, 31, "GL_ARB_vertex_attrib_binding" ), + FEAT(gpu_shader5, 40, 32, "GL_ARB_gpu_shader5", "GL_EXT_gpu_shader5", "GL_OES_gpu_shader5" ), + FEAT(images, 42, 31, "GL_ARB_shader_image_load_store" ), + FEAT(indep_blend, 30, 32, "GL_EXT_draw_buffers2", "GL_OES_draw_buffers_indexed" ), + FEAT(indep_blend_func, 40, 32, "GL_ARB_draw_buffers_blend", "GL_OES_draw_buffers_indexed"), + FEAT(indirect_draw, 40, 31, "GL_ARB_draw_indirect" ), + FEAT(multisample, 32, 30, "GL_ARB_texture_multisample" ), + FEAT(occlusion_query_boolean, 33, 30, "GL_EXT_occlusion_query_boolean", "GL_ARB_occlusion_query2"), + FEAT(robust_buffer_access, 43, UNAVAIL, "GL_ARB_robust_buffer_access_behavior", "GL_KHR_robust_buffer_access_behavior" ), + FEAT(sample_mask, 32, 31, "GL_ARB_texture_multisample" ), + FEAT(sample_shading, 40, 32, "GL_ARB_sample_shading", "GL_OES_sample_shading" ), + FEAT(samplers, 33, 30, "GL_ARB_sampler_objects" ), + FEAT(separate_shader_objects, 41, 31, "GL_ARB_seperate_shader_objects"), + FEAT(ssbo, 43, 31, "GL_ARB_shader_storage_buffer_object" ), + FEAT(ssbo_barrier, 43, 31, NULL), + FEAT(srgb_write_control, 30, UNAVAIL, "GL_EXT_sRGB_write_control"), + FEAT(stencil_texturing, 43, 31, "GL_ARB_stencil_texturing" ), + FEAT(storage_multisample, 43, 31, "GL_ARB_texture_storage_multisample" ), + FEAT(tessellation, 40, 32, "GL_ARB_tessellation_shader", "GL_OES_tessellation_shader", "GL_EXT_tessellation_shader" ), + FEAT(texture_array, 30, 30, "GL_EXT_texture_array" ), + FEAT(texture_buffer_range, 43, 32, "GL_ARB_texture_buffer_range" ), + FEAT(texture_gather, 40, 31, "GL_ARB_texture_gather" ), + FEAT(texture_multisample, 32, 30, "GL_ARB_texture_multisample" ), + FEAT(texture_srgb_decode, UNAVAIL, UNAVAIL, "GL_EXT_texture_sRGB_decode" ), + FEAT(texture_storage, 42, 30, "GL_ARB_texture_storage" ), + FEAT(transform_feedback, 30, 30, "GL_EXT_transform_feedback" ), + FEAT(transform_feedback2, 40, 30, "GL_ARB_transform_feedback2" ), + FEAT(ubo, 31, 30, "GL_ARB_uniform_buffer_object" ), +}; + +static bool features[feat_last]; +static bool features_initialized = false; + +static inline bool has_feature(enum features_id feature_id) +{ + return features[feature_id]; +} + +static inline void set_feature(enum features_id feature_id) +{ + features[feature_id] = true; +} + +struct vrend_linked_shader_program { + struct list_head head; + struct list_head sl[PIPE_SHADER_TYPES]; + GLuint id; + + bool dual_src_linked; + struct vrend_shader *ss[PIPE_SHADER_TYPES]; + + uint32_t ubo_used_mask[PIPE_SHADER_TYPES]; + uint32_t samplers_used_mask[PIPE_SHADER_TYPES]; + + GLuint *shadow_samp_mask_locs[PIPE_SHADER_TYPES]; + GLuint *shadow_samp_add_locs[PIPE_SHADER_TYPES]; + + GLint const_location[PIPE_SHADER_TYPES]; + + GLuint *attrib_locs; + uint32_t shadow_samp_mask[PIPE_SHADER_TYPES]; + + GLuint vs_ws_adjust_loc; + float viewport_neg_val; + + GLint fs_stipple_loc; + + GLuint clip_locs[8]; + + uint32_t images_used_mask[PIPE_SHADER_TYPES]; + GLint *img_locs[PIPE_SHADER_TYPES]; + + GLuint *ssbo_locs[PIPE_SHADER_TYPES]; + + struct vrend_sub_context *ref_context; +}; + +struct vrend_shader { + struct vrend_shader *next_variant; + struct vrend_shader_selector *sel; + + struct vrend_strarray glsl_strings; + GLuint id; + GLuint compiled_fs_id; + struct vrend_shader_key key; + struct list_head programs; +}; + +struct vrend_shader_selector { + struct pipe_reference reference; + + unsigned num_shaders; + unsigned type; + struct vrend_shader_info sinfo; + + struct vrend_shader *current; + struct tgsi_token *tokens; + + uint32_t req_local_mem; + char *tmp_buf; + uint32_t buf_len; + uint32_t buf_offset; +}; + +struct vrend_texture { + struct vrend_resource base; + struct pipe_sampler_state state; + GLenum cur_swizzle_r; + GLenum cur_swizzle_g; + GLenum cur_swizzle_b; + GLenum cur_swizzle_a; + GLuint cur_srgb_decode; + GLuint cur_base, cur_max; +}; + +struct vrend_surface { + struct pipe_reference reference; + GLuint id; + GLuint res_handle; + GLuint format; + GLuint val0, val1; + struct vrend_resource *texture; +}; + +struct vrend_sampler_state { + struct pipe_sampler_state base; + GLuint ids[2]; +}; + +struct vrend_so_target { + struct pipe_reference reference; + GLuint res_handle; + unsigned buffer_offset; + unsigned buffer_size; + struct vrend_resource *buffer; + struct vrend_sub_context *sub_ctx; +}; + +struct vrend_sampler_view { + struct pipe_reference reference; + GLuint id; + enum virgl_formats format; + GLenum target; + GLuint val0, val1; + GLuint gl_swizzle_r; + GLuint gl_swizzle_g; + GLuint gl_swizzle_b; + GLuint gl_swizzle_a; + GLuint srgb_decode; + struct vrend_resource *texture; +}; + +struct vrend_image_view { + GLuint id; + GLenum access; + GLenum format; + union { + struct { + unsigned first_layer:16; /**< first layer to use for array textures */ + unsigned last_layer:16; /**< last layer to use for array textures */ + unsigned level:8; /**< mipmap level to use */ + } tex; + struct { + unsigned offset; /**< offset in bytes */ + unsigned size; /**< size of the accessible sub-range in bytes */ + } buf; + } u; + struct vrend_resource *texture; +}; + +struct vrend_ssbo { + struct vrend_resource *res; + unsigned buffer_size; + unsigned buffer_offset; +}; + +struct vrend_abo { + struct vrend_resource *res; + unsigned buffer_size; + unsigned buffer_offset; +}; + +struct vrend_vertex_element { + struct pipe_vertex_element base; + GLenum type; + GLboolean norm; + GLuint nr_chan; +}; + +struct vrend_vertex_element_array { + unsigned count; + struct vrend_vertex_element elements[PIPE_MAX_ATTRIBS]; + GLuint id; +}; + +struct vrend_constants { + unsigned int *consts; + uint32_t num_consts; + uint32_t num_allocated_consts; +}; + +struct vrend_shader_view { + int num_views; + struct vrend_sampler_view *views[PIPE_MAX_SHADER_SAMPLER_VIEWS]; + uint32_t res_id[PIPE_MAX_SHADER_SAMPLER_VIEWS]; + uint32_t old_ids[PIPE_MAX_SHADER_SAMPLER_VIEWS]; +}; + +struct vrend_viewport { + GLint cur_x, cur_y; + GLsizei width, height; + GLfloat near_val, far_val; +}; + +/* create a streamout object to support pause/resume */ +struct vrend_streamout_object { + GLuint id; + uint32_t num_targets; + uint32_t handles[16]; + struct list_head head; + int xfb_state; + struct vrend_so_target *so_targets[16]; +}; + +#define XFB_STATE_OFF 0 +#define XFB_STATE_STARTED_NEED_BEGIN 1 +#define XFB_STATE_STARTED 2 +#define XFB_STATE_PAUSED 3 + +struct vrend_sub_context { + struct list_head head; + + virgl_gl_context gl_context; + + int sub_ctx_id; + + GLuint vaoid; + uint32_t enabled_attribs_bitmask; + + struct list_head programs; + struct util_hash_table *object_hash; + + struct vrend_vertex_element_array *ve; + int num_vbos; + int old_num_vbos; /* for cleaning up */ + struct pipe_vertex_buffer vbo[PIPE_MAX_ATTRIBS]; + uint32_t vbo_res_ids[PIPE_MAX_ATTRIBS]; + + struct pipe_index_buffer ib; + uint32_t index_buffer_res_id; + + bool vbo_dirty; + bool shader_dirty; + bool cs_shader_dirty; + bool stencil_state_dirty; + bool image_state_dirty; + bool blend_state_dirty; + + uint32_t long_shader_in_progress_handle[PIPE_SHADER_TYPES]; + struct vrend_shader_selector *shaders[PIPE_SHADER_TYPES]; + struct vrend_linked_shader_program *prog; + + int prog_ids[PIPE_SHADER_TYPES]; + struct vrend_shader_view views[PIPE_SHADER_TYPES]; + + struct vrend_constants consts[PIPE_SHADER_TYPES]; + bool const_dirty[PIPE_SHADER_TYPES]; + struct vrend_sampler_state *sampler_state[PIPE_SHADER_TYPES][PIPE_MAX_SAMPLERS]; + + struct pipe_constant_buffer cbs[PIPE_SHADER_TYPES][PIPE_MAX_CONSTANT_BUFFERS]; + uint32_t const_bufs_used_mask[PIPE_SHADER_TYPES]; + uint32_t const_bufs_dirty[PIPE_SHADER_TYPES]; + + int num_sampler_states[PIPE_SHADER_TYPES]; + + uint32_t sampler_views_dirty[PIPE_SHADER_TYPES]; + + uint32_t fb_id; + int nr_cbufs, old_nr_cbufs; + struct vrend_surface *zsurf; + struct vrend_surface *surf[PIPE_MAX_COLOR_BUFS]; + + struct vrend_viewport vps[PIPE_MAX_VIEWPORTS]; + /* viewport is negative */ + uint32_t scissor_state_dirty; + uint32_t viewport_state_dirty; + uint32_t viewport_state_initialized; + + uint32_t fb_height; + + struct pipe_scissor_state ss[PIPE_MAX_VIEWPORTS]; + + struct pipe_blend_state blend_state; + struct pipe_depth_stencil_alpha_state dsa_state; + struct pipe_rasterizer_state rs_state; + + uint8_t stencil_refs[2]; + bool viewport_is_negative; + /* this is set if the contents of the FBO look upside down when viewed + with 0,0 as the bottom corner */ + bool inverted_fbo_content; + + GLuint blit_fb_ids[2]; + + struct pipe_depth_stencil_alpha_state *dsa; + + struct pipe_clip_state ucp_state; + + bool depth_test_enabled; + bool stencil_test_enabled; + bool framebuffer_srgb_enabled; + + GLuint program_id; + int last_shader_idx; + + GLint draw_indirect_buffer; + + GLint draw_indirect_params_buffer; + + struct pipe_rasterizer_state hw_rs_state; + struct pipe_blend_state hw_blend_state; + + struct list_head streamout_list; + struct vrend_streamout_object *current_so; + + struct pipe_blend_color blend_color; + + uint32_t cond_render_q_id; + GLenum cond_render_gl_mode; + + struct vrend_image_view image_views[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_IMAGES]; + uint32_t images_used_mask[PIPE_SHADER_TYPES]; + + struct vrend_ssbo ssbo[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_BUFFERS]; + uint32_t ssbo_used_mask[PIPE_SHADER_TYPES]; + + struct vrend_abo abo[PIPE_MAX_HW_ATOMIC_BUFFERS]; + uint32_t abo_used_mask; + uint8_t swizzle_output_rgb_to_bgr; +}; + +struct vrend_context { + struct list_head sub_ctxs; + + struct vrend_sub_context *sub; + struct vrend_sub_context *sub0; + + int ctx_id; + /* has this ctx gotten an error? */ + bool in_error; + bool ctx_switch_pending; + bool pstip_inited; + + GLuint pstipple_tex_id; + + /* resource bounds to this context */ + struct util_hash_table *res_hash; + + struct list_head active_nontimer_query_list; + struct list_head ctx_entry; + + struct vrend_shader_cfg shader_cfg; + struct virgl_client *client; +}; + +static void vrend_update_viewport_state(struct vrend_context *ctx); +static void vrend_update_scissor_state(struct vrend_context *ctx); +static void vrend_destroy_query_object(void *obj_ptr); +static void vrend_finish_context_switch(struct vrend_context *ctx); +static void vrend_patch_blend_state(struct vrend_context *ctx); +static void vrend_update_frontface_state(struct vrend_context *ctx); +static void vrend_destroy_resource_object(void *obj_ptr); +static void vrend_renderer_detach_res_ctx(struct vrend_context *ctx, int res_handle); +static void vrend_destroy_program(struct vrend_linked_shader_program *ent); +static void vrend_apply_sampler_state(struct vrend_context *ctx, + struct vrend_resource *res, + uint32_t shader_type, + int id, int sampler_id, + struct vrend_sampler_view *tview); +static GLenum tgsitargettogltarget(const enum pipe_texture_target target, int nr_samples); + +void vrend_update_stencil_state(struct vrend_context *ctx); + +static struct vrend_format_table tex_conv_table[VIRGL_FORMAT_MAX_EXTENDED]; +static bool tex_conv_table_initialized = false; + +static inline bool vrend_format_can_sample(enum virgl_formats format) +{ + return tex_conv_table[format].bindings & VIRGL_BIND_SAMPLER_VIEW; +} + +static inline bool vrend_format_can_readback(enum virgl_formats format) +{ + return tex_conv_table[format].flags & VIRGL_TEXTURE_CAN_READBACK; +} + +static inline bool vrend_format_can_render(enum virgl_formats format) +{ + return tex_conv_table[format].bindings & VIRGL_BIND_RENDER_TARGET; +} + +static inline bool vrend_format_is_ds(enum virgl_formats format) +{ + return tex_conv_table[format].bindings & VIRGL_BIND_DEPTH_STENCIL; +} + +static bool vrend_blit_needs_swizzle(enum virgl_formats src, + enum virgl_formats dst) +{ + for (int i = 0; i < 4; ++i) { + if (tex_conv_table[src].swizzle[i] != tex_conv_table[dst].swizzle[i]) + return true; + } + return false; +} + +static inline GLenum translate_gles_emulation_texture_target(GLenum target) +{ + switch (target) { + case GL_TEXTURE_1D: + case GL_TEXTURE_RECTANGLE: return GL_TEXTURE_2D; + case GL_TEXTURE_1D_ARRAY: return GL_TEXTURE_2D_ARRAY; + default: return target; + } +} + +static inline const char *pipe_shader_to_prefix(int shader_type) +{ + switch (shader_type) { + case PIPE_SHADER_VERTEX: return "vs"; + case PIPE_SHADER_FRAGMENT: return "fs"; + case PIPE_SHADER_GEOMETRY: return "gs"; + case PIPE_SHADER_TESS_CTRL: return "tc"; + case PIPE_SHADER_TESS_EVAL: return "te"; + case PIPE_SHADER_COMPUTE: return "cs"; + default: + return NULL; + }; +} + +static void init_features(int gles_ver) +{ + for (enum features_id id = 0; id < feat_last; id++) { + if (gles_ver >= feature_list[id].gles_ver) { + set_feature(id); + } else { + for (uint32_t i = 0; i < FEAT_MAX_EXTS; i++) { + if (!feature_list[id].gl_ext[i]) + break; + if (vrend_has_gl_extension(feature_list[id].gl_ext[i])) { + set_feature(id); + break; + } + } + } + } +} + +static void vrend_destroy_surface(struct vrend_surface *surf) +{ + if (surf->id != surf->texture->id) + glDeleteTextures(1, &surf->id); + vrend_resource_reference(&surf->texture, NULL); + free(surf); +} + +static inline void +vrend_surface_reference(struct vrend_surface **ptr, struct vrend_surface *surf) +{ + struct vrend_surface *old_surf = *ptr; + + if (pipe_reference(&(*ptr)->reference, &surf->reference)) + vrend_destroy_surface(old_surf); + *ptr = surf; +} + +static void vrend_destroy_sampler_view(struct vrend_sampler_view *samp) +{ + if (samp->texture->id != samp->id) + glDeleteTextures(1, &samp->id); + vrend_resource_reference(&samp->texture, NULL); + free(samp); +} + +static inline void +vrend_sampler_view_reference(struct vrend_sampler_view **ptr, struct vrend_sampler_view *view) +{ + struct vrend_sampler_view *old_view = *ptr; + + if (pipe_reference(&(*ptr)->reference, &view->reference)) + vrend_destroy_sampler_view(old_view); + *ptr = view; +} + +static void vrend_destroy_so_target(struct vrend_so_target *target) +{ + vrend_resource_reference(&target->buffer, NULL); + free(target); +} + +static inline void +vrend_so_target_reference(struct vrend_so_target **ptr, struct vrend_so_target *target) +{ + struct vrend_so_target *old_target = *ptr; + + if (pipe_reference(&(*ptr)->reference, &target->reference)) + vrend_destroy_so_target(old_target); + *ptr = target; +} + +static void vrend_shader_destroy(struct vrend_shader *shader) +{ + struct vrend_linked_shader_program *ent, *tmp; + + LIST_FOR_EACH_ENTRY_SAFE(ent, tmp, &shader->programs, sl[shader->sel->type]) { + vrend_destroy_program(ent); + } + + glDeleteShader(shader->id); + strarray_free(&shader->glsl_strings, true); + free(shader); +} + +static void vrend_destroy_shader_selector(struct vrend_shader_selector *sel) +{ + struct vrend_shader *p = sel->current, *c; + unsigned i; + while (p) { + c = p->next_variant; + vrend_shader_destroy(p); + p = c; + } + if (sel->sinfo.so_names) + for (i = 0; i < sel->sinfo.so_info.num_outputs; i++) + free(sel->sinfo.so_names[i]); + free(sel->tmp_buf); + free(sel->sinfo.so_names); + free(sel->sinfo.interpinfo); + free(sel->sinfo.sampler_arrays); + free(sel->sinfo.image_arrays); + free(sel->tokens); + free(sel); +} + +static bool vrend_compile_shader(struct vrend_context *ctx, + struct vrend_shader *shader) +{ + GLint param; + const char *shader_parts[SHADER_MAX_STRINGS]; + + for (int i = 0; i < shader->glsl_strings.num_strings; i++) + shader_parts[i] = shader->glsl_strings.strings[i].buf; + glShaderSource(shader->id, shader->glsl_strings.num_strings, shader_parts, NULL); + glCompileShader(shader->id); + glGetShaderiv(shader->id, GL_COMPILE_STATUS, ¶m); + if (param == GL_FALSE) + return false; + return true; +} + +static inline void +vrend_shader_state_reference(struct vrend_shader_selector **ptr, struct vrend_shader_selector *shader) +{ + struct vrend_shader_selector *old_shader = *ptr; + + if (pipe_reference(&(*ptr)->reference, &shader->reference)) + vrend_destroy_shader_selector(old_shader); + *ptr = shader; +} + +void +vrend_insert_format(struct vrend_format_table *entry, uint32_t bindings, uint32_t flags) +{ + tex_conv_table[entry->format] = *entry; + tex_conv_table[entry->format].bindings = bindings; + tex_conv_table[entry->format].flags = flags; +} + +void +vrend_insert_format_swizzle(int override_format, struct vrend_format_table *entry, + uint32_t bindings, uint8_t swizzle[4], uint32_t flags) +{ + int i; + tex_conv_table[override_format] = *entry; + tex_conv_table[override_format].bindings = bindings; + tex_conv_table[override_format].flags = flags | VIRGL_TEXTURE_NEED_SWIZZLE; + for (i = 0; i < 4; i++) + tex_conv_table[override_format].swizzle[i] = swizzle[i]; +} + +const struct vrend_format_table * +vrend_get_format_table_entry(enum virgl_formats format) +{ + return &tex_conv_table[format]; +} + +static void vrend_use_program(struct vrend_context *ctx, GLuint program_id) +{ + if (ctx->sub->program_id != program_id) { + glUseProgram(program_id); + ctx->sub->program_id = program_id; + } +} + +static void vrend_init_pstipple_texture(struct vrend_context *ctx) +{ + glGenTextures(1, &ctx->pstipple_tex_id); + glBindTexture(GL_TEXTURE_2D, ctx->pstipple_tex_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 32, 32, 0, GL_RED, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + ctx->pstip_inited = true; +} + +static void vrend_depth_test_enable(struct vrend_context *ctx, bool depth_test_enable) +{ + if (ctx->sub->depth_test_enabled != depth_test_enable) { + ctx->sub->depth_test_enabled = depth_test_enable; + if (depth_test_enable) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + } +} + +static void vrend_stencil_test_enable(struct vrend_context *ctx, bool stencil_test_enable) +{ + if (ctx->sub->stencil_test_enabled != stencil_test_enable) { + ctx->sub->stencil_test_enabled = stencil_test_enable; + if (stencil_test_enable) + glEnable(GL_STENCIL_TEST); + else + glDisable(GL_STENCIL_TEST); + } +} + +static int bind_sampler_locs(struct vrend_linked_shader_program *sprog, + int id, int next_sampler_id) +{ + if (sprog->ss[id]->sel->sinfo.samplers_used_mask) { + uint32_t mask = sprog->ss[id]->sel->sinfo.samplers_used_mask; + int nsamp = util_bitcount(sprog->ss[id]->sel->sinfo.samplers_used_mask); + int index; + sprog->shadow_samp_mask[id] = sprog->ss[id]->sel->sinfo.shadow_samp_mask; + if (sprog->ss[id]->sel->sinfo.shadow_samp_mask) { + sprog->shadow_samp_mask_locs[id] = calloc(nsamp, sizeof(uint32_t)); + sprog->shadow_samp_add_locs[id] = calloc(nsamp, sizeof(uint32_t)); + } else { + sprog->shadow_samp_mask_locs[id] = sprog->shadow_samp_add_locs[id] = NULL; + } + const char *prefix = pipe_shader_to_prefix(id); + index = 0; + while(mask) { + uint32_t i = u_bit_scan(&mask); + char name[64]; + if (sprog->ss[id]->sel->sinfo.num_sampler_arrays) { + int arr_idx = vrend_shader_lookup_sampler_array(&sprog->ss[id]->sel->sinfo, i); + snprintf(name, 32, "%ssamp%d[%d]", prefix, arr_idx, i - arr_idx); + } else + snprintf(name, 32, "%ssamp%d", prefix, i); + + glUniform1i(glGetUniformLocation(sprog->id, name), next_sampler_id++); + + if (sprog->ss[id]->sel->sinfo.shadow_samp_mask & (1 << i)) { + snprintf(name, 32, "%sshadmask%d", prefix, i); + sprog->shadow_samp_mask_locs[id][index] = glGetUniformLocation(sprog->id, name); + snprintf(name, 32, "%sshadadd%d", prefix, i); + sprog->shadow_samp_add_locs[id][index] = glGetUniformLocation(sprog->id, name); + } + index++; + } + } else { + sprog->shadow_samp_mask_locs[id] = NULL; + sprog->shadow_samp_add_locs[id] = NULL; + sprog->shadow_samp_mask[id] = 0; + } + sprog->samplers_used_mask[id] = sprog->ss[id]->sel->sinfo.samplers_used_mask; + + return next_sampler_id; +} + +static void bind_const_locs(struct vrend_linked_shader_program *sprog, + int id) +{ + if (sprog->ss[id]->sel->sinfo.num_consts) { + char name[32]; + snprintf(name, 32, "%sconst0", pipe_shader_to_prefix(id)); + sprog->const_location[id] = glGetUniformLocation(sprog->id, name); + } else + sprog->const_location[id] = -1; +} + +static int bind_ubo_locs(struct vrend_linked_shader_program *sprog, + int id, int next_ubo_id) +{ + if (!has_feature(feat_ubo)) + return next_ubo_id; + if (sprog->ss[id]->sel->sinfo.ubo_used_mask) { + const char *prefix = pipe_shader_to_prefix(id); + + unsigned mask = sprog->ss[id]->sel->sinfo.ubo_used_mask; + while (mask) { + uint32_t ubo_idx = u_bit_scan(&mask); + char name[32]; + if (sprog->ss[id]->sel->sinfo.ubo_indirect) + snprintf(name, 32, "%subo[%d]", prefix, ubo_idx - 1); + else + snprintf(name, 32, "%subo%d", prefix, ubo_idx); + + GLuint loc = glGetUniformBlockIndex(sprog->id, name); + glUniformBlockBinding(sprog->id, loc, next_ubo_id++); + } + } + + sprog->ubo_used_mask[id] = sprog->ss[id]->sel->sinfo.ubo_used_mask; + + return next_ubo_id; +} + +static void bind_ssbo_locs(struct vrend_linked_shader_program *sprog, + int id) +{ + int i; + char name[32]; + if (!has_feature(feat_ssbo)) + return; + if (sprog->ss[id]->sel->sinfo.ssbo_used_mask) { + const char *prefix = pipe_shader_to_prefix(id); + uint32_t mask = sprog->ss[id]->sel->sinfo.ssbo_used_mask; + sprog->ssbo_locs[id] = calloc(util_last_bit(mask), sizeof(uint32_t)); + + while (mask) { + i = u_bit_scan(&mask); + snprintf(name, 32, "%sssbo%d", prefix, i); + sprog->ssbo_locs[id][i] = glGetProgramResourceIndex(sprog->id, GL_SHADER_STORAGE_BLOCK, name); + } + } else + sprog->ssbo_locs[id] = NULL; +} + +static void bind_image_locs(struct vrend_linked_shader_program *sprog, + int id) +{ + int i; + char name[32]; + const char *prefix = pipe_shader_to_prefix(id); + + uint32_t mask = sprog->ss[id]->sel->sinfo.images_used_mask; + if (!mask && ! sprog->ss[id]->sel->sinfo.num_image_arrays) + return; + + if (!has_feature(feat_images)) + return; + + int nsamp = util_last_bit(mask); + if (nsamp) { + sprog->img_locs[id] = calloc(nsamp, sizeof(GLint)); + if (!sprog->img_locs[id]) + return; + } else + sprog->img_locs[id] = NULL; + + if (sprog->ss[id]->sel->sinfo.num_image_arrays) { + for (i = 0; i < sprog->ss[id]->sel->sinfo.num_image_arrays; i++) { + struct vrend_array *img_array = &sprog->ss[id]->sel->sinfo.image_arrays[i]; + for (int j = 0; j < img_array->array_size; j++) { + snprintf(name, 32, "%simg%d[%d]", prefix, img_array->first, j); + sprog->img_locs[id][img_array->first + j] = glGetUniformLocation(sprog->id, name); + } + } + } else if (mask) { + for (i = 0; i < nsamp; i++) { + if (mask & (1 << i)) { + snprintf(name, 32, "%simg%d", prefix, i); + sprog->img_locs[id][i] = glGetUniformLocation(sprog->id, name); + } else { + sprog->img_locs[id][i] = -1; + } + } + } + sprog->images_used_mask[id] = mask; +} + +static struct vrend_linked_shader_program *add_cs_shader_program(struct vrend_context *ctx, + struct vrend_shader *cs) +{ + struct vrend_linked_shader_program *sprog = CALLOC_STRUCT(vrend_linked_shader_program); + GLuint prog_id; + GLint lret; + prog_id = glCreateProgram(); + glAttachShader(prog_id, cs->id); + glLinkProgram(prog_id); + + glGetProgramiv(prog_id, GL_LINK_STATUS, &lret); + if (lret == GL_FALSE) { + glDeleteProgram(prog_id); + free(sprog); + return NULL; + } + sprog->ss[PIPE_SHADER_COMPUTE] = cs; + + list_add(&sprog->sl[PIPE_SHADER_COMPUTE], &cs->programs); + sprog->id = prog_id; + list_addtail(&sprog->head, &ctx->sub->programs); + + vrend_use_program(ctx, prog_id); + + bind_sampler_locs(sprog, PIPE_SHADER_COMPUTE, 0); + bind_ubo_locs(sprog, PIPE_SHADER_COMPUTE, 0); + bind_ssbo_locs(sprog, PIPE_SHADER_COMPUTE); + bind_const_locs(sprog, PIPE_SHADER_COMPUTE); + bind_image_locs(sprog, PIPE_SHADER_COMPUTE); + return sprog; +} + +static struct vrend_linked_shader_program *add_shader_program(struct vrend_context *ctx, + struct vrend_shader *vs, + struct vrend_shader *fs, + struct vrend_shader *gs, + struct vrend_shader *tcs, + struct vrend_shader *tes) +{ + struct vrend_linked_shader_program *sprog = CALLOC_STRUCT(vrend_linked_shader_program); + char name[64]; + int i; + GLuint prog_id; + GLint lret; + int id; + int last_shader; + bool do_patch = false; + if (!sprog) + return NULL; + + /* need to rewrite VS code to add interpolation params */ + if (gs && gs->compiled_fs_id != fs->id) + do_patch = true; + if (!gs && tes && tes->compiled_fs_id != fs->id) + do_patch = true; + if (!gs && !tes && vs->compiled_fs_id != fs->id) + do_patch = true; + + if (do_patch) { + bool ret; + + if (gs) + vrend_patch_vertex_shader_interpolants(ctx, &ctx->shader_cfg, &gs->glsl_strings, + &gs->sel->sinfo, + &fs->sel->sinfo, "gso", fs->key.flatshade); + else if (tes) + vrend_patch_vertex_shader_interpolants(ctx, &ctx->shader_cfg, &tes->glsl_strings, + &tes->sel->sinfo, + &fs->sel->sinfo, "teo", fs->key.flatshade); + else + vrend_patch_vertex_shader_interpolants(ctx, &ctx->shader_cfg, &vs->glsl_strings, + &vs->sel->sinfo, + &fs->sel->sinfo, "vso", fs->key.flatshade); + ret = vrend_compile_shader(ctx, gs ? gs : (tes ? tes : vs)); + if (ret == false) { + glDeleteShader(gs ? gs->id : (tes ? tes->id : vs->id)); + free(sprog); + return NULL; + } + if (gs) + gs->compiled_fs_id = fs->id; + else if (tes) + tes->compiled_fs_id = fs->id; + else + vs->compiled_fs_id = fs->id; + } + + prog_id = glCreateProgram(); + glAttachShader(prog_id, vs->id); + if (tcs && tcs->id > 0) + glAttachShader(prog_id, tcs->id); + if (tes && tes->id > 0) + glAttachShader(prog_id, tes->id); + + glAttachShader(prog_id, fs->id); + + sprog->dual_src_linked = false; + + if (has_feature(feat_gles31_vertex_attrib_binding)) { + uint32_t mask = vs->sel->sinfo.attrib_input_mask; + while (mask) { + i = u_bit_scan(&mask); + snprintf(name, 32, "in_%d", i); + glBindAttribLocation(prog_id, i, name); + } + } + + glLinkProgram(prog_id); + + glGetProgramiv(prog_id, GL_LINK_STATUS, &lret); + if (lret == GL_FALSE) { + glDeleteProgram(prog_id); + free(sprog); + return NULL; + } + + sprog->ss[PIPE_SHADER_VERTEX] = vs; + sprog->ss[PIPE_SHADER_FRAGMENT] = fs; + sprog->ss[PIPE_SHADER_GEOMETRY] = gs; + sprog->ss[PIPE_SHADER_TESS_CTRL] = tcs; + sprog->ss[PIPE_SHADER_TESS_EVAL] = tes; + + list_add(&sprog->sl[PIPE_SHADER_VERTEX], &vs->programs); + list_add(&sprog->sl[PIPE_SHADER_FRAGMENT], &fs->programs); + if (gs) + list_add(&sprog->sl[PIPE_SHADER_GEOMETRY], &gs->programs); + if (tcs) + list_add(&sprog->sl[PIPE_SHADER_TESS_CTRL], &tcs->programs); + if (tes) + list_add(&sprog->sl[PIPE_SHADER_TESS_EVAL], &tes->programs); + + last_shader = tes ? PIPE_SHADER_TESS_EVAL : (gs ? PIPE_SHADER_GEOMETRY : PIPE_SHADER_FRAGMENT); + sprog->id = prog_id; + + list_addtail(&sprog->head, &ctx->sub->programs); + + if (fs->key.pstipple_tex) + sprog->fs_stipple_loc = glGetUniformLocation(prog_id, "pstipple_sampler"); + else + sprog->fs_stipple_loc = -1; + sprog->vs_ws_adjust_loc = glGetUniformLocation(prog_id, "winsys_adjust_y"); + + vrend_use_program(ctx, prog_id); + + int next_ubo_id = 0, next_sampler_id = 0; + for (id = PIPE_SHADER_VERTEX; id <= last_shader; id++) { + if (!sprog->ss[id]) + continue; + + next_sampler_id = bind_sampler_locs(sprog, id, next_sampler_id); + bind_const_locs(sprog, id); + next_ubo_id = bind_ubo_locs(sprog, id, next_ubo_id); + bind_image_locs(sprog, id); + bind_ssbo_locs(sprog, id); + } + + if (!has_feature(feat_gles31_vertex_attrib_binding)) { + if (vs->sel->sinfo.num_inputs) { + sprog->attrib_locs = calloc(vs->sel->sinfo.num_inputs, sizeof(uint32_t)); + if (sprog->attrib_locs) { + for (i = 0; i < vs->sel->sinfo.num_inputs; i++) { + snprintf(name, 32, "in_%d", i); + sprog->attrib_locs[i] = glGetAttribLocation(prog_id, name); + } + } + } else + sprog->attrib_locs = NULL; + } + + if (vs->sel->sinfo.num_ucp) { + for (i = 0; i < vs->sel->sinfo.num_ucp; i++) { + snprintf(name, 32, "clipp[%d]", i); + sprog->clip_locs[i] = glGetUniformLocation(prog_id, name); + } + } + return sprog; +} + +static struct vrend_linked_shader_program *lookup_cs_shader_program(struct vrend_context *ctx, + GLuint cs_id) +{ + struct vrend_linked_shader_program *ent; + LIST_FOR_EACH_ENTRY(ent, &ctx->sub->programs, head) { + if (!ent->ss[PIPE_SHADER_COMPUTE]) + continue; + if (ent->ss[PIPE_SHADER_COMPUTE]->id == cs_id) + return ent; + } + return NULL; +} + +static struct vrend_linked_shader_program *lookup_shader_program(struct vrend_context *ctx, + GLuint vs_id, + GLuint fs_id, + GLuint gs_id, + GLuint tcs_id, + GLuint tes_id, + bool dual_src) +{ + struct vrend_linked_shader_program *ent; + LIST_FOR_EACH_ENTRY(ent, &ctx->sub->programs, head) { + if (ent->dual_src_linked != dual_src) + continue; + if (ent->ss[PIPE_SHADER_COMPUTE]) + continue; + if (ent->ss[PIPE_SHADER_VERTEX]->id != vs_id) + continue; + if (ent->ss[PIPE_SHADER_FRAGMENT]->id != fs_id) + continue; + if (ent->ss[PIPE_SHADER_GEOMETRY] && + ent->ss[PIPE_SHADER_GEOMETRY]->id != gs_id) + continue; + if (ent->ss[PIPE_SHADER_TESS_CTRL] && + ent->ss[PIPE_SHADER_TESS_CTRL]->id != tcs_id) + continue; + if (ent->ss[PIPE_SHADER_TESS_EVAL] && + ent->ss[PIPE_SHADER_TESS_EVAL]->id != tes_id) + continue; + return ent; + } + return NULL; +} + +static void vrend_destroy_program(struct vrend_linked_shader_program *ent) +{ + int i; + if (ent->ref_context && ent->ref_context->prog == ent) + ent->ref_context->prog = NULL; + + glDeleteProgram(ent->id); + list_del(&ent->head); + + for (i = PIPE_SHADER_VERTEX; i <= PIPE_SHADER_COMPUTE; i++) { + if (ent->ss[i]) + list_del(&ent->sl[i]); + free(ent->shadow_samp_mask_locs[i]); + free(ent->shadow_samp_add_locs[i]); + free(ent->ssbo_locs[i]); + free(ent->img_locs[i]); + } + free(ent->attrib_locs); + free(ent); +} + +static void vrend_free_programs(struct vrend_sub_context *sub) +{ + struct vrend_linked_shader_program *ent, *tmp; + + if (LIST_IS_EMPTY(&sub->programs)) + return; + + LIST_FOR_EACH_ENTRY_SAFE(ent, tmp, &sub->programs, head) { + vrend_destroy_program(ent); + } +} + +static void vrend_destroy_streamout_object(struct vrend_streamout_object *obj) +{ + unsigned i; + list_del(&obj->head); + for (i = 0; i < obj->num_targets; i++) + vrend_so_target_reference(&obj->so_targets[i], NULL); + if (has_feature(feat_transform_feedback2)) + glDeleteTransformFeedbacks(1, &obj->id); + FREE(obj); +} + +int vrend_create_surface(struct vrend_context *ctx, + uint32_t handle, + uint32_t res_handle, uint32_t format, + uint32_t val0, uint32_t val1) +{ + struct vrend_surface *surf; + struct vrend_resource *res; + uint32_t ret_handle; + + if (format >= PIPE_FORMAT_COUNT) { + return EINVAL; + } + + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + if (!res) + return EINVAL; + + surf = CALLOC_STRUCT(vrend_surface); + if (!surf) + return ENOMEM; + + surf->res_handle = res_handle; + surf->format = format; + + surf->val0 = val0; + surf->val1 = val1; + surf->id = res->id; + + pipe_reference_init(&surf->reference, 1); + + vrend_resource_reference(&surf->texture, res); + + ret_handle = vrend_renderer_object_insert(ctx, surf, sizeof(*surf), handle, VIRGL_OBJECT_SURFACE); + if (ret_handle == 0) { + FREE(surf); + return ENOMEM; + } + return 0; +} + +static void vrend_destroy_surface_object(void *obj_ptr) +{ + struct vrend_surface *surface = obj_ptr; + + vrend_surface_reference(&surface, NULL); +} + +static void vrend_destroy_sampler_view_object(void *obj_ptr) +{ + struct vrend_sampler_view *samp = obj_ptr; + + vrend_sampler_view_reference(&samp, NULL); +} + +static void vrend_destroy_so_target_object(void *obj_ptr) +{ + struct vrend_so_target *target = obj_ptr; + struct vrend_sub_context *sub_ctx = target->sub_ctx; + struct vrend_streamout_object *obj, *tmp; + bool found; + unsigned i; + + LIST_FOR_EACH_ENTRY_SAFE(obj, tmp, &sub_ctx->streamout_list, head) { + found = false; + for (i = 0; i < obj->num_targets; i++) { + if (obj->so_targets[i] == target) { + found = true; + break; + } + } + if (found) { + if (obj == sub_ctx->current_so) + sub_ctx->current_so = NULL; + if (obj->xfb_state == XFB_STATE_PAUSED) { + if (has_feature(feat_transform_feedback2)) + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id); + glEndTransformFeedback(); + if (sub_ctx->current_so && has_feature(feat_transform_feedback2)) + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, sub_ctx->current_so->id); + } + vrend_destroy_streamout_object(obj); + } + } + + vrend_so_target_reference(&target, NULL); +} + +static void vrend_destroy_vertex_elements_object(void *obj_ptr) +{ + struct vrend_vertex_element_array *v = obj_ptr; + + if (has_feature(feat_gles31_vertex_attrib_binding)) { + glDeleteVertexArrays(1, &v->id); + } + FREE(v); +} + +static void vrend_destroy_sampler_state_object(void *obj_ptr) +{ + struct vrend_sampler_state *state = obj_ptr; + + if (has_feature(feat_samplers)) + glDeleteSamplers(2, state->ids); + FREE(state); +} + +static GLuint convert_wrap(int wrap) +{ + switch(wrap){ + case PIPE_TEX_WRAP_REPEAT: return GL_REPEAT; + case PIPE_TEX_WRAP_CLAMP:; + case PIPE_TEX_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + case PIPE_TEX_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + + case PIPE_TEX_WRAP_MIRROR_REPEAT: return GL_MIRRORED_REPEAT; + case PIPE_TEX_WRAP_MIRROR_CLAMP: + case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_EDGE: + case PIPE_TEX_WRAP_MIRROR_CLAMP_TO_BORDER: return GL_MIRROR_CLAMP_TO_EDGE_EXT; + default: + assert(0); + return -1; + } +} + +static inline GLenum convert_mag_filter(unsigned int filter) +{ + if (filter == PIPE_TEX_FILTER_NEAREST) + return GL_NEAREST; + return GL_LINEAR; +} + +static inline GLenum convert_min_filter(unsigned int filter, unsigned int mip_filter) +{ + if (mip_filter == PIPE_TEX_MIPFILTER_NONE) + return convert_mag_filter(filter); + else if (mip_filter == PIPE_TEX_MIPFILTER_LINEAR) { + if (filter == PIPE_TEX_FILTER_NEAREST) + return GL_NEAREST_MIPMAP_LINEAR; + else + return GL_LINEAR_MIPMAP_LINEAR; + } else if (mip_filter == PIPE_TEX_MIPFILTER_NEAREST) { + if (filter == PIPE_TEX_FILTER_NEAREST) + return GL_NEAREST_MIPMAP_NEAREST; + else + return GL_LINEAR_MIPMAP_NEAREST; + } + assert(0); + return 0; +} + +int vrend_create_sampler_state(struct vrend_context *ctx, + uint32_t handle, + struct pipe_sampler_state *templ) +{ + struct vrend_sampler_state *state = CALLOC_STRUCT(vrend_sampler_state); + int ret_handle; + + if (!state) + return ENOMEM; + + state->base = *templ; + + if (has_feature(feat_samplers)) { + glGenSamplers(2, state->ids); + + for (int i = 0; i < 2; ++i) { + glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_S, convert_wrap(templ->wrap_s)); + glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_T, convert_wrap(templ->wrap_t)); + glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_R, convert_wrap(templ->wrap_r)); + glSamplerParameterf(state->ids[i], GL_TEXTURE_MIN_FILTER, convert_min_filter(templ->min_img_filter, templ->min_mip_filter)); + glSamplerParameterf(state->ids[i], GL_TEXTURE_MAG_FILTER, convert_mag_filter(templ->mag_img_filter)); + glSamplerParameterf(state->ids[i], GL_TEXTURE_MIN_LOD, templ->min_lod); + glSamplerParameterf(state->ids[i], GL_TEXTURE_MAX_LOD, templ->max_lod); + glSamplerParameteri(state->ids[i], GL_TEXTURE_COMPARE_FUNC, GL_NEVER + templ->compare_func); + glSamplerParameterIuiv(state->ids[i], GL_TEXTURE_BORDER_COLOR, templ->border_color.ui); + glSamplerParameteri(state->ids[i], GL_TEXTURE_SRGB_DECODE_EXT, i == 0 ? GL_SKIP_DECODE_EXT : GL_DECODE_EXT); + } + } + ret_handle = vrend_renderer_object_insert(ctx, state, sizeof(struct vrend_sampler_state), handle, + VIRGL_OBJECT_SAMPLER_STATE); + if (!ret_handle) { + if (has_feature(feat_samplers)) + glDeleteSamplers(2, state->ids); + FREE(state); + return ENOMEM; + } + return 0; +} + +static inline GLenum to_gl_swizzle(int swizzle) +{ + switch (swizzle) { + case PIPE_SWIZZLE_RED: return GL_RED; + case PIPE_SWIZZLE_GREEN: return GL_GREEN; + case PIPE_SWIZZLE_BLUE: return GL_BLUE; + case PIPE_SWIZZLE_ALPHA: return GL_ALPHA; + case PIPE_SWIZZLE_ZERO: return GL_ZERO; + case PIPE_SWIZZLE_ONE: return GL_ONE; + default: + assert(0); + return 0; + } +} + +int vrend_create_sampler_view(struct vrend_context *ctx, + uint32_t handle, + uint32_t res_handle, uint32_t format, + uint32_t val0, uint32_t val1, uint32_t swizzle_packed) +{ + struct vrend_sampler_view *view; + struct vrend_resource *res; + int ret_handle; + uint8_t swizzle[4]; + + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + if (!res) + return EINVAL; + + view = CALLOC_STRUCT(vrend_sampler_view); + if (!view) + return ENOMEM; + + pipe_reference_init(&view->reference, 1); + view->format = format & 0xffffff; + + if (!view->format || view->format >= VIRGL_FORMAT_MAX) { + FREE(view); + return EINVAL; + } + + uint32_t pipe_target = (format >> 24) & 0xff; + if (pipe_target >= PIPE_MAX_TEXTURE_TYPES) { + FREE(view); + return EINVAL; + } + + view->target = tgsitargettogltarget(pipe_target, res->base.nr_samples); + view->target = translate_gles_emulation_texture_target(view->target); + + view->val0 = val0; + view->val1 = val1; + + swizzle[0] = swizzle_packed & 0x7; + swizzle[1] = (swizzle_packed >> 3) & 0x7; + swizzle[2] = (swizzle_packed >> 6) & 0x7; + swizzle[3] = (swizzle_packed >> 9) & 0x7; + + vrend_resource_reference(&view->texture, res); + + view->id = view->texture->id; + if (view->target == PIPE_BUFFER) + view->target = view->texture->target; + + view->srgb_decode = GL_DECODE_EXT; + if (view->format != view->texture->base.format) { + if (util_format_is_srgb(view->texture->base.format) && + !util_format_is_srgb(view->format)) + view->srgb_decode = GL_SKIP_DECODE_EXT; + } + + if (!(util_format_has_alpha(view->format) || util_format_is_depth_or_stencil(view->format))) { + if (swizzle[0] == PIPE_SWIZZLE_ALPHA) + swizzle[0] = PIPE_SWIZZLE_ONE; + if (swizzle[1] == PIPE_SWIZZLE_ALPHA) + swizzle[1] = PIPE_SWIZZLE_ONE; + if (swizzle[2] == PIPE_SWIZZLE_ALPHA) + swizzle[2] = PIPE_SWIZZLE_ONE; + if (swizzle[3] == PIPE_SWIZZLE_ALPHA) + swizzle[3] = PIPE_SWIZZLE_ONE; + } + + if (tex_conv_table[view->format].flags & VIRGL_TEXTURE_NEED_SWIZZLE) { + if (swizzle[0] <= PIPE_SWIZZLE_ALPHA) + swizzle[0] = tex_conv_table[view->format].swizzle[swizzle[0]]; + if (swizzle[1] <= PIPE_SWIZZLE_ALPHA) + swizzle[1] = tex_conv_table[view->format].swizzle[swizzle[1]]; + if (swizzle[2] <= PIPE_SWIZZLE_ALPHA) + swizzle[2] = tex_conv_table[view->format].swizzle[swizzle[2]]; + if (swizzle[3] <= PIPE_SWIZZLE_ALPHA) + swizzle[3] = tex_conv_table[view->format].swizzle[swizzle[3]]; + } + + view->gl_swizzle_r = to_gl_swizzle(swizzle[0]); + view->gl_swizzle_g = to_gl_swizzle(swizzle[1]); + view->gl_swizzle_b = to_gl_swizzle(swizzle[2]); + view->gl_swizzle_a = to_gl_swizzle(swizzle[3]); + + ret_handle = vrend_renderer_object_insert(ctx, view, sizeof(*view), handle, VIRGL_OBJECT_SAMPLER_VIEW); + if (ret_handle == 0) { + FREE(view); + return ENOMEM; + } + return 0; +} + +void vrend_fb_bind_texture_id(struct vrend_resource *res, + int id, + int idx, + uint32_t level, uint32_t layer) +{ + const struct util_format_description *desc = util_format_description(res->base.format); + GLenum attachment = GL_COLOR_ATTACHMENT0 + idx; + + if (vrend_format_is_ds(res->base.format)) { + if (util_format_has_stencil(desc)) { + if (util_format_has_depth(desc)) + attachment = GL_DEPTH_STENCIL_ATTACHMENT; + else + attachment = GL_STENCIL_ATTACHMENT; + } else + attachment = GL_DEPTH_ATTACHMENT; + } + + switch (res->target) { + case GL_TEXTURE_1D_ARRAY: + case GL_TEXTURE_2D_ARRAY: + case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: + case GL_TEXTURE_CUBE_MAP_ARRAY: + if (layer == 0xffffffff) + glFramebufferTexture(GL_FRAMEBUFFER, attachment, + id, level); + else + glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, + id, level, layer); + break; + case GL_TEXTURE_3D: + if (layer == 0xffffffff) + glFramebufferTexture(GL_FRAMEBUFFER, attachment, + id, level); + break; + case GL_TEXTURE_CUBE_MAP: + if (layer == 0xffffffff) + glFramebufferTexture(GL_FRAMEBUFFER, attachment, + id, level); + else + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, + GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer, id, level); + break; + case GL_TEXTURE_1D: + case GL_TEXTURE_2D: + default: + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, + res->target, id, level); + break; + } + + if (attachment == GL_DEPTH_ATTACHMENT) { + switch (res->target) { + case GL_TEXTURE_1D: + case GL_TEXTURE_2D: + default: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + break; + } + } +} + +void vrend_fb_bind_texture(struct vrend_resource *res, + int idx, + uint32_t level, uint32_t layer) +{ + vrend_fb_bind_texture_id(res, res->id, idx, level, layer); +} + +static void vrend_hw_set_zsurf_texture(struct vrend_context *ctx) +{ + struct vrend_surface *surf = ctx->sub->zsurf; + + if (!surf) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + } else { + uint32_t first_layer = surf->val1 & 0xffff; + uint32_t last_layer = (surf->val1 >> 16) & 0xffff; + + if (!surf->texture) + return; + + vrend_fb_bind_texture_id(surf->texture, surf->id, 0, surf->val0, + first_layer != last_layer ? 0xffffffff : first_layer); + } +} + +static void vrend_hw_set_color_surface(struct vrend_context *ctx, int index) +{ + struct vrend_surface *surf = ctx->sub->surf[index]; + + if (!surf) { + GLenum attachment = GL_COLOR_ATTACHMENT0 + index; + + glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, + GL_TEXTURE_2D, 0, 0); + } else { + uint32_t first_layer = ctx->sub->surf[index]->val1 & 0xffff; + uint32_t last_layer = (ctx->sub->surf[index]->val1 >> 16) & 0xffff; + + vrend_fb_bind_texture_id(surf->texture, surf->id, index, surf->val0, + first_layer != last_layer ? 0xffffffff : first_layer); + } +} + +static void vrend_hw_emit_framebuffer_state(struct vrend_context *ctx) +{ + static const GLenum buffers[8] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3, + GL_COLOR_ATTACHMENT4, + GL_COLOR_ATTACHMENT5, + GL_COLOR_ATTACHMENT6, + GL_COLOR_ATTACHMENT7, + }; + + if (ctx->sub->nr_cbufs == 0) { + glReadBuffer(GL_NONE); + if (has_feature(feat_srgb_write_control)) { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + ctx->sub->framebuffer_srgb_enabled = false; + } + } else if (has_feature(feat_srgb_write_control)) { + struct vrend_surface *surf = NULL; + bool use_srgb = false; + int i; + for (i = 0; i < ctx->sub->nr_cbufs; i++) { + if (ctx->sub->surf[i]) { + surf = ctx->sub->surf[i]; + if (util_format_is_srgb(surf->format)) { + use_srgb = true; + } + } + } + if (use_srgb) { + glEnable(GL_FRAMEBUFFER_SRGB_EXT); + } else { + glDisable(GL_FRAMEBUFFER_SRGB_EXT); + } + ctx->sub->framebuffer_srgb_enabled = use_srgb; + } + + glDrawBuffers(ctx->sub->nr_cbufs, buffers); +} + +void vrend_set_framebuffer_state(struct vrend_context *ctx, + uint32_t nr_cbufs, uint32_t surf_handle[PIPE_MAX_COLOR_BUFS], + uint32_t zsurf_handle) +{ + struct vrend_surface *surf, *zsurf; + int i; + int old_num; + GLint new_height = -1; + bool new_ibf = false; + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->fb_id); + + if (zsurf_handle) { + zsurf = vrend_object_lookup(ctx->sub->object_hash, zsurf_handle, VIRGL_OBJECT_SURFACE); + if (!zsurf) + return; + } else + zsurf = NULL; + + if (ctx->sub->zsurf != zsurf) { + vrend_surface_reference(&ctx->sub->zsurf, zsurf); + vrend_hw_set_zsurf_texture(ctx); + } + + old_num = ctx->sub->nr_cbufs; + ctx->sub->nr_cbufs = nr_cbufs; + ctx->sub->old_nr_cbufs = old_num; + + for (i = 0; i < (int)nr_cbufs; i++) { + if (surf_handle[i] != 0) { + surf = vrend_object_lookup(ctx->sub->object_hash, surf_handle[i], VIRGL_OBJECT_SURFACE); + if (!surf) + return; + } else + surf = NULL; + + if (ctx->sub->surf[i] != surf) { + vrend_surface_reference(&ctx->sub->surf[i], surf); + vrend_hw_set_color_surface(ctx, i); + } + } + + if (old_num > ctx->sub->nr_cbufs) { + for (i = ctx->sub->nr_cbufs; i < old_num; i++) { + vrend_surface_reference(&ctx->sub->surf[i], NULL); + vrend_hw_set_color_surface(ctx, i); + } + } + + /* find a buffer to set fb_height from */ + if (ctx->sub->nr_cbufs == 0 && !ctx->sub->zsurf) { + new_height = 0; + new_ibf = false; + } else if (ctx->sub->nr_cbufs == 0) { + new_height = u_minify(ctx->sub->zsurf->texture->base.height0, ctx->sub->zsurf->val0); + new_ibf = ctx->sub->zsurf->texture->y_0_top ? true : false; + } + else { + surf = NULL; + for (i = 0; i < ctx->sub->nr_cbufs; i++) { + if (ctx->sub->surf[i]) { + surf = ctx->sub->surf[i]; + break; + } + } + if (surf == NULL) + return; + new_height = u_minify(surf->texture->base.height0, surf->val0); + new_ibf = surf->texture->y_0_top ? true : false; + } + + if (new_height != -1) { + if (ctx->sub->fb_height != (uint32_t)new_height || ctx->sub->inverted_fbo_content != new_ibf) { + ctx->sub->fb_height = new_height; + ctx->sub->inverted_fbo_content = new_ibf; + ctx->sub->viewport_state_dirty = (1 << 0); + } + } + + vrend_hw_emit_framebuffer_state(ctx); + + ctx->sub->shader_dirty = true; + ctx->sub->blend_state_dirty = true; +} + +void vrend_set_framebuffer_state_no_attach(UNUSED struct vrend_context *ctx, + uint32_t width, uint32_t height, + uint32_t layers, uint32_t samples) +{ + if (has_feature(feat_fb_no_attach)) { + glFramebufferParameteri(GL_FRAMEBUFFER, + GL_FRAMEBUFFER_DEFAULT_WIDTH, width); + glFramebufferParameteri(GL_FRAMEBUFFER, + GL_FRAMEBUFFER_DEFAULT_HEIGHT, height); + glFramebufferParameteri(GL_FRAMEBUFFER, + GL_FRAMEBUFFER_DEFAULT_LAYERS, layers); + glFramebufferParameteri(GL_FRAMEBUFFER, + GL_FRAMEBUFFER_DEFAULT_SAMPLES, samples); + } +} + +/* + * if the viewport Y scale factor is > 0 then we are rendering to + * an FBO already so don't need to invert rendering? + */ +void vrend_set_viewport_states(struct vrend_context *ctx, + uint32_t start_slot, + uint32_t num_viewports, + const struct pipe_viewport_state *state) +{ + /* convert back to glViewport */ + GLint x, y; + GLsizei width, height; + GLfloat near_val, far_val; + bool viewport_is_negative = (state[0].scale[1] < 0) ? true : false; + uint i, idx; + + if (num_viewports > PIPE_MAX_VIEWPORTS || + start_slot > (PIPE_MAX_VIEWPORTS - num_viewports)) + return; + + for (i = 0; i < num_viewports; i++) { + GLfloat abs_s1 = fabsf(state[i].scale[1]); + + idx = start_slot + i; + width = state[i].scale[0] * 2.0f; + height = abs_s1 * 2.0f; + x = state[i].translate[0] - state[i].scale[0]; + y = state[i].translate[1] - state[i].scale[1]; + + if (!ctx->sub->rs_state.clip_halfz) { + near_val = state[i].translate[2] - state[i].scale[2]; + far_val = near_val + (state[i].scale[2] * 2.0); + } else { + near_val = state[i].translate[2]; + far_val = state[i].scale[2] + state[i].translate[2]; + } + + if (ctx->sub->vps[idx].cur_x != x || + ctx->sub->vps[idx].cur_y != y || + ctx->sub->vps[idx].width != width || + ctx->sub->vps[idx].height != height || + ctx->sub->vps[idx].near_val != near_val || + ctx->sub->vps[idx].far_val != far_val || + (!(ctx->sub->viewport_state_initialized &= (1 << idx)))) { + ctx->sub->vps[idx].cur_x = x; + ctx->sub->vps[idx].cur_y = y; + ctx->sub->vps[idx].width = width; + ctx->sub->vps[idx].height = height; + ctx->sub->vps[idx].near_val = near_val; + ctx->sub->vps[idx].far_val = far_val; + ctx->sub->viewport_state_dirty |= (1 << idx); + } + + if (idx == 0) { + if (ctx->sub->viewport_is_negative != viewport_is_negative) + ctx->sub->viewport_is_negative = viewport_is_negative; + } + } +} + +int vrend_create_vertex_elements_state(struct vrend_context *ctx, + uint32_t handle, + unsigned num_elements, + const struct pipe_vertex_element *elements) +{ + struct vrend_vertex_element_array *v; + const struct util_format_description *desc; + GLenum type; + uint i; + uint32_t ret_handle; + + if (num_elements > PIPE_MAX_ATTRIBS) + return EINVAL; + + v = CALLOC_STRUCT(vrend_vertex_element_array); + if (!v) + return ENOMEM; + + v->count = num_elements; + for (i = 0; i < num_elements; i++) { + memcpy(&v->elements[i].base, &elements[i], sizeof(struct pipe_vertex_element)); + + desc = util_format_description(elements[i].src_format); + if (!desc) { + FREE(v); + return EINVAL; + } + + type = GL_FALSE; + if (desc->channel[0].type == UTIL_FORMAT_TYPE_FLOAT) { + if (desc->channel[0].size == 32) + type = GL_FLOAT; + else if (desc->channel[0].size == 64) + type = GL_FLOAT; + else if (desc->channel[0].size == 16) + type = GL_HALF_FLOAT; + } else if (desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED && + desc->channel[0].size == 8) + type = GL_UNSIGNED_BYTE; + else if (desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED && + desc->channel[0].size == 8) + type = GL_BYTE; + else if (desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED && + desc->channel[0].size == 16) + type = GL_UNSIGNED_SHORT; + else if (desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED && + desc->channel[0].size == 16) + type = GL_SHORT; + else if (desc->channel[0].type == UTIL_FORMAT_TYPE_UNSIGNED && + desc->channel[0].size == 32) + type = GL_UNSIGNED_INT; + else if (desc->channel[0].type == UTIL_FORMAT_TYPE_SIGNED && + desc->channel[0].size == 32) + type = GL_INT; + else if (elements[i].src_format == PIPE_FORMAT_R10G10B10A2_SSCALED || + elements[i].src_format == PIPE_FORMAT_R10G10B10A2_SNORM || + elements[i].src_format == PIPE_FORMAT_B10G10R10A2_SNORM) + type = GL_INT_2_10_10_10_REV; + else if (elements[i].src_format == PIPE_FORMAT_R10G10B10A2_USCALED || + elements[i].src_format == PIPE_FORMAT_R10G10B10A2_UNORM || + elements[i].src_format == PIPE_FORMAT_B10G10R10A2_UNORM) + type = GL_UNSIGNED_INT_2_10_10_10_REV; + else if (elements[i].src_format == PIPE_FORMAT_R11G11B10_FLOAT) + type = GL_UNSIGNED_INT_10F_11F_11F_REV; + + if (type == GL_FALSE) { + FREE(v); + return EINVAL; + } + + v->elements[i].type = type; + if (desc->channel[0].normalized) + v->elements[i].norm = GL_TRUE; + + if (desc->nr_channels == 4 && desc->swizzle[0] == UTIL_FORMAT_SWIZZLE_Z) + v->elements[i].nr_chan = 4; + else if (elements[i].src_format == PIPE_FORMAT_R11G11B10_FLOAT) + v->elements[i].nr_chan = 3; + else + v->elements[i].nr_chan = desc->nr_channels; + } + + if (has_feature(feat_gles31_vertex_attrib_binding)) { + glGenVertexArrays(1, &v->id); + glBindVertexArray(v->id); + for (i = 0; i < num_elements; i++) { + struct vrend_vertex_element *ve = &v->elements[i]; + + if (util_format_is_pure_integer(ve->base.src_format)) + glVertexAttribIFormat(i, ve->nr_chan, ve->type, ve->base.src_offset); + else + glVertexAttribFormat(i, ve->nr_chan, ve->type, ve->norm, ve->base.src_offset); + glVertexAttribBinding(i, ve->base.vertex_buffer_index); + glVertexBindingDivisor(i, ve->base.instance_divisor); + glEnableVertexAttribArray(i); + } + } + ret_handle = vrend_renderer_object_insert(ctx, v, sizeof(struct vrend_vertex_element), handle, + VIRGL_OBJECT_VERTEX_ELEMENTS); + if (!ret_handle) { + FREE(v); + return ENOMEM; + } + return 0; +} + +void vrend_bind_vertex_elements_state(struct vrend_context *ctx, + uint32_t handle) +{ + struct vrend_vertex_element_array *v; + + if (!handle) { + ctx->sub->ve = NULL; + return; + } + v = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_VERTEX_ELEMENTS); + if (!v) + return; + + if (ctx->sub->ve != v) + ctx->sub->vbo_dirty = true; + ctx->sub->ve = v; +} + +void vrend_set_constants(struct vrend_context *ctx, + uint32_t shader, + UNUSED uint32_t index, + uint32_t num_constant, + float *data) +{ + struct vrend_constants *consts; + + consts = &ctx->sub->consts[shader]; + ctx->sub->const_dirty[shader] = true; + + /* avoid reallocations by only growing the buffer */ + if (consts->num_allocated_consts < num_constant) { + free(consts->consts); + consts->consts = malloc(num_constant * sizeof(float)); + if (!consts->consts) + return; + consts->num_allocated_consts = num_constant; + } + + memcpy(consts->consts, data, num_constant * sizeof(unsigned int)); + consts->num_consts = num_constant; +} + +void vrend_set_uniform_buffer(struct vrend_context *ctx, + uint32_t shader, + uint32_t index, + uint32_t offset, + uint32_t length, + uint32_t res_handle) +{ + struct vrend_resource *res; + + if (!has_feature(feat_ubo)) + return; + + if (res_handle) { + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + + if (!res) + return; + ctx->sub->cbs[shader][index].buffer = (struct pipe_resource *)res; + ctx->sub->cbs[shader][index].buffer_offset = offset; + ctx->sub->cbs[shader][index].buffer_size = length; + + ctx->sub->const_bufs_used_mask[shader] |= (1u << index); + } else { + ctx->sub->cbs[shader][index].buffer = NULL; + ctx->sub->cbs[shader][index].buffer_offset = 0; + ctx->sub->cbs[shader][index].buffer_size = 0; + ctx->sub->const_bufs_used_mask[shader] &= ~(1u << index); + } + ctx->sub->const_bufs_dirty[shader] |= (1u << index); +} + +void vrend_set_index_buffer(struct vrend_context *ctx, + uint32_t res_handle, + uint32_t index_size, + uint32_t offset) +{ + struct vrend_resource *res; + + ctx->sub->ib.index_size = index_size; + ctx->sub->ib.offset = offset; + if (res_handle) { + if (ctx->sub->index_buffer_res_id != res_handle) { + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + if (!res) { + vrend_resource_reference((struct vrend_resource **)&ctx->sub->ib.buffer, NULL); + ctx->sub->index_buffer_res_id = 0; + return; + } + vrend_resource_reference((struct vrend_resource **)&ctx->sub->ib.buffer, res); + ctx->sub->index_buffer_res_id = res_handle; + } + } else { + vrend_resource_reference((struct vrend_resource **)&ctx->sub->ib.buffer, NULL); + ctx->sub->index_buffer_res_id = 0; + } +} + +void vrend_set_single_vbo(struct vrend_context *ctx, + uint32_t index, + uint32_t stride, + uint32_t buffer_offset, + uint32_t res_handle) +{ + struct vrend_resource *res; + + if (ctx->sub->vbo[index].stride != stride || + ctx->sub->vbo[index].buffer_offset != buffer_offset || + ctx->sub->vbo_res_ids[index] != res_handle) + ctx->sub->vbo_dirty = true; + + ctx->sub->vbo[index].stride = stride; + ctx->sub->vbo[index].buffer_offset = buffer_offset; + + if (res_handle == 0) { + vrend_resource_reference((struct vrend_resource **)&ctx->sub->vbo[index].buffer, NULL); + ctx->sub->vbo_res_ids[index] = 0; + } else if (ctx->sub->vbo_res_ids[index] != res_handle) { + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + if (!res) { + ctx->sub->vbo_res_ids[index] = 0; + return; + } + vrend_resource_reference((struct vrend_resource **)&ctx->sub->vbo[index].buffer, res); + ctx->sub->vbo_res_ids[index] = res_handle; + } +} + +void vrend_set_num_vbo(struct vrend_context *ctx, + int num_vbo) +{ + int old_num = ctx->sub->num_vbos; + int i; + + ctx->sub->num_vbos = num_vbo; + ctx->sub->old_num_vbos = old_num; + + if (old_num != num_vbo) + ctx->sub->vbo_dirty = true; + + for (i = num_vbo; i < old_num; i++) { + vrend_resource_reference((struct vrend_resource **)&ctx->sub->vbo[i].buffer, NULL); + ctx->sub->vbo_res_ids[i] = 0; + } + +} + +void vrend_set_single_sampler_view(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t index, + uint32_t handle) +{ + struct vrend_sampler_view *view = NULL; + struct vrend_texture *tex; + + if (handle) { + view = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_SAMPLER_VIEW); + if (!view) { + ctx->sub->views[shader_type].views[index] = NULL; + return; + } + if (ctx->sub->views[shader_type].views[index] == view) { + return; + } + /* we should have a reference to this texture taken at create time */ + tex = (struct vrend_texture *)view->texture; + if (!tex) { + return; + } + + ctx->sub->sampler_views_dirty[shader_type] |= 1u << index; + + if (!has_bit(view->texture->storage_bits, VREND_STORAGE_GL_BUFFER)) { + if (view->texture->id == view->id) { + glBindTexture(view->target, view->id); + + if (util_format_is_depth_or_stencil(view->format)) { + if (has_feature(feat_stencil_texturing)) { + const struct util_format_description *desc = util_format_description(view->format); + if (!util_format_has_depth(desc)) { + glTexParameteri(view->texture->target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); + } else { + glTexParameteri(view->texture->target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); + } + } + } + + GLuint base_level = view->val1 & 0xff; + GLuint max_level = (view->val1 >> 8) & 0xff; + + if (tex->cur_base != base_level) { + glTexParameteri(view->texture->target, GL_TEXTURE_BASE_LEVEL, base_level); + tex->cur_base = base_level; + } + if (tex->cur_max != max_level) { + glTexParameteri(view->texture->target, GL_TEXTURE_MAX_LEVEL, max_level); + tex->cur_max = max_level; + } + if (tex->cur_swizzle_r != view->gl_swizzle_r) { + glTexParameteri(view->texture->target, GL_TEXTURE_SWIZZLE_R, view->gl_swizzle_r); + tex->cur_swizzle_r = view->gl_swizzle_r; + } + if (tex->cur_swizzle_g != view->gl_swizzle_g) { + glTexParameteri(view->texture->target, GL_TEXTURE_SWIZZLE_G, view->gl_swizzle_g); + tex->cur_swizzle_g = view->gl_swizzle_g; + } + if (tex->cur_swizzle_b != view->gl_swizzle_b) { + glTexParameteri(view->texture->target, GL_TEXTURE_SWIZZLE_B, view->gl_swizzle_b); + tex->cur_swizzle_b = view->gl_swizzle_b; + } + if (tex->cur_swizzle_a != view->gl_swizzle_a) { + glTexParameteri(view->texture->target, GL_TEXTURE_SWIZZLE_A, view->gl_swizzle_a); + tex->cur_swizzle_a = view->gl_swizzle_a; + } + if (tex->cur_srgb_decode != view->srgb_decode && util_format_is_srgb(tex->base.base.format)) { + if (has_feature(feat_samplers)) + ctx->sub->sampler_views_dirty[shader_type] |= (1u << index); + else if (has_feature(feat_texture_srgb_decode)) { + glTexParameteri(view->texture->target, GL_TEXTURE_SRGB_DECODE_EXT, + view->srgb_decode); + tex->cur_srgb_decode = view->srgb_decode; + } + } + } + } else { + GLenum internalformat; + + if (!view->texture->tbo_tex_id) + glGenTextures(1, &view->texture->tbo_tex_id); + + glBindTexture(GL_TEXTURE_BUFFER, view->texture->tbo_tex_id); + internalformat = tex_conv_table[view->format].internalformat; + if (has_feature(feat_texture_buffer_range)) { + unsigned offset = view->val0; + unsigned size = view->val1 - view->val0 + 1; + int blsize = util_format_get_blocksize(view->format); + + offset *= blsize; + size *= blsize; + glTexBufferRange(GL_TEXTURE_BUFFER, internalformat, view->texture->id, offset, size); + } else + glTexBuffer(GL_TEXTURE_BUFFER, internalformat, view->texture->id); + } + } + + vrend_sampler_view_reference(&ctx->sub->views[shader_type].views[index], view); +} + +void vrend_set_num_sampler_views(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t start_slot, + uint32_t num_sampler_views) +{ + int last_slot = start_slot + num_sampler_views; + int i; + + for (i = last_slot; i < ctx->sub->views[shader_type].num_views; i++) + vrend_sampler_view_reference(&ctx->sub->views[shader_type].views[i], NULL); + + ctx->sub->views[shader_type].num_views = last_slot; +} + +void vrend_set_single_image_view(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t index, + uint32_t format, uint32_t access, + uint32_t layer_offset, uint32_t level_size, + uint32_t handle) +{ + struct vrend_image_view *iview = &ctx->sub->image_views[shader_type][index]; + struct vrend_resource *res; + + if (handle) { + if (!has_feature(feat_images)) + return; + + res = vrend_renderer_ctx_res_lookup(ctx, handle); + if (!res) + return; + iview->texture = res; + iview->format = tex_conv_table[format].internalformat; + iview->access = access; + iview->u.buf.offset = layer_offset; + iview->u.buf.size = level_size; + ctx->sub->images_used_mask[shader_type] |= (1u << index); + } else { + iview->texture = NULL; + iview->format = 0; + ctx->sub->images_used_mask[shader_type] &= ~(1u << index); + } +} + +void vrend_set_single_ssbo(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t index, + uint32_t offset, uint32_t length, + uint32_t handle) +{ + struct vrend_ssbo *ssbo = &ctx->sub->ssbo[shader_type][index]; + struct vrend_resource *res; + + if (!has_feature(feat_ssbo)) + return; + + if (handle) { + res = vrend_renderer_ctx_res_lookup(ctx, handle); + if (!res) + return; + ssbo->res = res; + ssbo->buffer_offset = offset; + ssbo->buffer_size = length; + ctx->sub->ssbo_used_mask[shader_type] |= (1u << index); + } else { + ssbo->res = 0; + ssbo->buffer_offset = 0; + ssbo->buffer_size = 0; + ctx->sub->ssbo_used_mask[shader_type] &= ~(1u << index); + } +} + +void vrend_set_single_abo(struct vrend_context *ctx, + uint32_t index, + uint32_t offset, uint32_t length, + uint32_t handle) +{ + struct vrend_abo *abo = &ctx->sub->abo[index]; + struct vrend_resource *res; + + if (!has_feature(feat_atomic_counters)) + return; + + if (handle) { + res = vrend_renderer_ctx_res_lookup(ctx, handle); + if (!res) + return; + abo->res = res; + abo->buffer_offset = offset; + abo->buffer_size = length; + ctx->sub->abo_used_mask |= (1u << index); + } else { + abo->res = 0; + abo->buffer_offset = 0; + abo->buffer_size = 0; + ctx->sub->abo_used_mask &= ~(1u << index); + } +} + +void vrend_memory_barrier(UNUSED struct vrend_context *ctx, + unsigned flags) +{ + GLbitfield gl_barrier = 0; + + if (!has_feature(feat_barrier)) + return; + + if ((flags & PIPE_BARRIER_ALL) == PIPE_BARRIER_ALL) + gl_barrier = GL_ALL_BARRIER_BITS; + else { + if (flags & PIPE_BARRIER_VERTEX_BUFFER) + gl_barrier |= GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT; + if (flags & PIPE_BARRIER_INDEX_BUFFER) + gl_barrier |= GL_ELEMENT_ARRAY_BARRIER_BIT; + if (flags & PIPE_BARRIER_CONSTANT_BUFFER) + gl_barrier |= GL_UNIFORM_BARRIER_BIT; + if (flags & PIPE_BARRIER_TEXTURE) + gl_barrier |= GL_TEXTURE_FETCH_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT; + if (flags & PIPE_BARRIER_IMAGE) + gl_barrier |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + if (flags & PIPE_BARRIER_INDIRECT_BUFFER) + gl_barrier |= GL_COMMAND_BARRIER_BIT; + if (flags & PIPE_BARRIER_FRAMEBUFFER) + gl_barrier |= GL_FRAMEBUFFER_BARRIER_BIT; + if (flags & PIPE_BARRIER_STREAMOUT_BUFFER) + gl_barrier |= GL_TRANSFORM_FEEDBACK_BARRIER_BIT; + if (flags & PIPE_BARRIER_SHADER_BUFFER) { + gl_barrier |= GL_ATOMIC_COUNTER_BARRIER_BIT; + if (has_feature(feat_ssbo_barrier)) + gl_barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + } + } + glMemoryBarrier(gl_barrier); +} + +static void vrend_destroy_shader_object(void *obj_ptr) +{ + struct vrend_shader_selector *state = obj_ptr; + + vrend_shader_state_reference(&state, NULL); +} + +static inline bool can_emulate_logicop(enum pipe_logicop op) +{ + if (has_feature(feat_framebuffer_fetch)) + return true; + + /* These ops don't need to read back from the framebuffer */ + switch (op) { + case PIPE_LOGICOP_CLEAR: + case PIPE_LOGICOP_COPY: + case PIPE_LOGICOP_SET: + case PIPE_LOGICOP_COPY_INVERTED: + return true; + default: + return false; + } +} + +static inline void vrend_fill_shader_key(struct vrend_context *ctx, + struct vrend_shader_selector *sel, + struct vrend_shader_key *key) +{ + unsigned type = sel->type; + + int i; + bool add_alpha_test = true; + key->cbufs_are_a8_bitmask = 0; + for (i = 0; i < ctx->sub->nr_cbufs; i++) { + if (!ctx->sub->surf[i]) + continue; + if (util_format_is_pure_integer(ctx->sub->surf[i]->format)) + add_alpha_test = false; + key->surface_component_bits[i] = util_format_get_component_bits(ctx->sub->surf[i]->format, UTIL_FORMAT_COLORSPACE_RGB, 0); + } + if (add_alpha_test) { + key->add_alpha_test = ctx->sub->dsa_state.alpha.enabled; + key->alpha_test = ctx->sub->dsa_state.alpha.func; + key->alpha_ref_val = ctx->sub->dsa_state.alpha.ref_value; + } + + key->pstipple_tex = ctx->sub->rs_state.poly_stipple_enable; + key->color_two_side = ctx->sub->rs_state.light_twoside; + + key->clip_plane_enable = ctx->sub->rs_state.clip_plane_enable; + key->flatshade = ctx->sub->rs_state.flatshade ? true : false; + + if (type == PIPE_SHADER_FRAGMENT && can_emulate_logicop(ctx->sub->blend_state.logicop_func)) { + key->fs_logicop_enabled = ctx->sub->blend_state.logicop_enable; + key->fs_logicop_func = ctx->sub->blend_state.logicop_func; + key->fs_logicop_emulate_coherent = true; + } + + key->coord_replace = ctx->sub->rs_state.point_quad_rasterization ? ctx->sub->rs_state.sprite_coord_enable : 0; + + if (type == PIPE_SHADER_FRAGMENT) + key->fs_swizzle_output_rgb_to_bgr = ctx->sub->swizzle_output_rgb_to_bgr; + + if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY]) + key->gs_present = true; + if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]) + key->tcs_present = true; + if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) + key->tes_present = true; + + int prev_type = -1; + + /* Gallium sends and binds the shaders in the reverse order, so if an + * old shader is still bound we should ignore the "previous" (as in + * execution order) shader when the key is evaluated, unless the currently + * bound shader selector is actually refers to the current shader. */ + if (ctx->sub->shaders[type] == sel) { + switch (type) { + case PIPE_SHADER_GEOMETRY: + if (key->tcs_present || key->tes_present) + prev_type = PIPE_SHADER_TESS_EVAL; + else + prev_type = PIPE_SHADER_VERTEX; + break; + case PIPE_SHADER_FRAGMENT: + if (key->gs_present) + prev_type = PIPE_SHADER_GEOMETRY; + else if (key->tcs_present || key->tes_present) + prev_type = PIPE_SHADER_TESS_EVAL; + else + prev_type = PIPE_SHADER_VERTEX; + break; + case PIPE_SHADER_TESS_EVAL: + if (key->tcs_present) + prev_type = PIPE_SHADER_TESS_CTRL; + else + prev_type = PIPE_SHADER_VERTEX; + break; + case PIPE_SHADER_TESS_CTRL: + prev_type = PIPE_SHADER_VERTEX; + break; + default: + break; + } + } + + if (prev_type != -1 && ctx->sub->shaders[prev_type]) { + key->prev_stage_pervertex_out = ctx->sub->shaders[prev_type]->sinfo.has_pervertex_out; + key->prev_stage_num_clip_out = ctx->sub->shaders[prev_type]->sinfo.num_clip_out; + key->prev_stage_num_cull_out = ctx->sub->shaders[prev_type]->sinfo.num_cull_out; + key->num_indirect_generic_inputs = ctx->sub->shaders[prev_type]->sinfo.num_indirect_generic_outputs; + key->num_indirect_patch_inputs = ctx->sub->shaders[prev_type]->sinfo.num_indirect_patch_outputs; + key->num_prev_generic_and_patch_outputs = ctx->sub->shaders[prev_type]->sinfo.num_generic_and_patch_outputs; + key->guest_sent_io_arrays = ctx->sub->shaders[prev_type]->sinfo.guest_sent_io_arrays; + + memcpy(key->prev_stage_generic_and_patch_outputs_layout, + ctx->sub->shaders[prev_type]->sinfo.generic_outputs_layout, + 64 * sizeof (struct vrend_layout_info)); + } + + int next_type = -1; + switch (type) { + case PIPE_SHADER_VERTEX: + if (key->tcs_present) + next_type = PIPE_SHADER_TESS_CTRL; + else if (key->gs_present) + next_type = PIPE_SHADER_GEOMETRY; + else if (key->tes_present) + next_type = PIPE_SHADER_TESS_CTRL; + else + next_type = PIPE_SHADER_FRAGMENT; + break; + case PIPE_SHADER_TESS_CTRL: + next_type = PIPE_SHADER_TESS_EVAL; + break; + case PIPE_SHADER_GEOMETRY: + next_type = PIPE_SHADER_FRAGMENT; + break; + case PIPE_SHADER_TESS_EVAL: + if (key->gs_present) + next_type = PIPE_SHADER_GEOMETRY; + else + next_type = PIPE_SHADER_FRAGMENT; + } + + if (next_type != -1 && ctx->sub->shaders[next_type]) { + key->num_indirect_generic_outputs = ctx->sub->shaders[next_type]->sinfo.num_indirect_generic_inputs; + key->num_indirect_patch_outputs = ctx->sub->shaders[next_type]->sinfo.num_indirect_patch_inputs; + key->generic_outputs_expected_mask = ctx->sub->shaders[next_type]->sinfo.generic_inputs_emitted_mask; + } +} + +static inline int conv_shader_type(int type) +{ + switch (type) { + case PIPE_SHADER_VERTEX: return GL_VERTEX_SHADER; + case PIPE_SHADER_FRAGMENT: return GL_FRAGMENT_SHADER; + case PIPE_SHADER_GEOMETRY: return GL_GEOMETRY_SHADER; + case PIPE_SHADER_TESS_CTRL: return GL_TESS_CONTROL_SHADER; + case PIPE_SHADER_TESS_EVAL: return GL_TESS_EVALUATION_SHADER; + case PIPE_SHADER_COMPUTE: return GL_COMPUTE_SHADER; + default: + return 0; + } +} + +static int vrend_shader_create(struct vrend_context *ctx, + struct vrend_shader *shader, + struct vrend_shader_key key) +{ + + shader->id = glCreateShader(conv_shader_type(shader->sel->type)); + shader->compiled_fs_id = 0; + + if (shader->sel->tokens) { + bool ret = vrend_convert_shader(ctx, &ctx->shader_cfg, shader->sel->tokens, + shader->sel->req_local_mem, &key, &shader->sel->sinfo, &shader->glsl_strings); + if (!ret) { + glDeleteShader(shader->id); + return -1; + } + } + + shader->key = key; + bool ret; + + ret = vrend_compile_shader(ctx, shader); + if (!ret) { + glDeleteShader(shader->id); + strarray_free(&shader->glsl_strings, true); + return -1; + } + return 0; +} + +static int vrend_shader_select(struct vrend_context *ctx, + struct vrend_shader_selector *sel, + bool *dirty) +{ + struct vrend_shader_key key; + struct vrend_shader *shader = NULL; + int r; + + memset(&key, 0, sizeof(key)); + vrend_fill_shader_key(ctx, sel, &key); + + if (sel->current && !memcmp(&sel->current->key, &key, sizeof(key))) + return 0; + + if (sel->num_shaders > 1) { + struct vrend_shader *p = sel->current; + struct vrend_shader *c = p->next_variant; + while (c && memcmp(&c->key, &key, sizeof(key)) != 0) { + p = c; + c = c->next_variant; + } + if (c) { + p->next_variant = c->next_variant; + shader = c; + } + } + + if (!shader) { + shader = CALLOC_STRUCT(vrend_shader); + shader->sel = sel; + list_inithead(&shader->programs); + strarray_alloc(&shader->glsl_strings, SHADER_MAX_STRINGS); + + r = vrend_shader_create(ctx, shader, key); + if (r) { + sel->current = NULL; + FREE(shader); + return r; + } + sel->num_shaders++; + } + if (dirty) + *dirty = true; + + shader->next_variant = sel->current; + sel->current = shader; + return 0; +} + +static void *vrend_create_shader_state(UNUSED struct vrend_context *ctx, + const struct pipe_stream_output_info *so_info, + uint32_t req_local_mem, + unsigned pipe_shader_type) +{ + struct vrend_shader_selector *sel = CALLOC_STRUCT(vrend_shader_selector); + + if (!sel) + return NULL; + + sel->req_local_mem = req_local_mem; + sel->type = pipe_shader_type; + sel->sinfo.so_info = *so_info; + pipe_reference_init(&sel->reference, 1); + + return sel; +} + +static int vrend_finish_shader(struct vrend_context *ctx, + struct vrend_shader_selector *sel, + const struct tgsi_token *tokens) +{ + int r; + + sel->tokens = tgsi_dup_tokens(tokens); + + r = vrend_shader_select(ctx, sel, NULL); + if (r) { + return EINVAL; + } + return 0; +} + +int vrend_create_shader(struct vrend_context *ctx, + uint32_t handle, + const struct pipe_stream_output_info *so_info, + uint32_t req_local_mem, + const char *shd_text, uint32_t offlen, uint32_t num_tokens, + uint32_t type, uint32_t pkt_length) +{ + struct vrend_shader_selector *sel = NULL; + int ret_handle; + bool new_shader = true, long_shader = false; + bool finished = false; + int ret; + + if (type > PIPE_SHADER_COMPUTE) + return EINVAL; + + if (type == PIPE_SHADER_GEOMETRY && + !has_feature(feat_geometry_shader)) + return EINVAL; + + if ((type == PIPE_SHADER_TESS_CTRL || + type == PIPE_SHADER_TESS_EVAL) && + !has_feature(feat_tessellation)) + return EINVAL; + + if (type == PIPE_SHADER_COMPUTE && + !has_feature(feat_compute_shader)) + return EINVAL; + + if (offlen & VIRGL_OBJ_SHADER_OFFSET_CONT) + new_shader = false; + else if (((offlen + 3) / 4) > pkt_length) + long_shader = true; + + /* if we have an in progress one - don't allow a new shader + of that type or a different handle. */ + if (ctx->sub->long_shader_in_progress_handle[type]) { + if (new_shader == true) + return EINVAL; + if (handle != ctx->sub->long_shader_in_progress_handle[type]) + return EINVAL; + } + + if (new_shader) { + sel = vrend_create_shader_state(ctx, so_info, req_local_mem, type); + if (sel == NULL) + return ENOMEM; + + if (long_shader) { + sel->buf_len = ((offlen + 3) / 4) * 4; /* round up buffer size */ + sel->tmp_buf = malloc(sel->buf_len); + if (!sel->tmp_buf) { + ret = ENOMEM; + goto error; + } + memcpy(sel->tmp_buf, shd_text, pkt_length * 4); + sel->buf_offset = pkt_length * 4; + ctx->sub->long_shader_in_progress_handle[type] = handle; + } else + finished = true; + } else { + sel = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_SHADER); + if (!sel) { + ret = EINVAL; + goto error; + } + + offlen &= ~VIRGL_OBJ_SHADER_OFFSET_CONT; + if (offlen != sel->buf_offset) { + ret = EINVAL; + goto error; + } + + /*make sure no overflow */ + if (pkt_length * 4 < pkt_length || + pkt_length * 4 + sel->buf_offset < pkt_length * 4 || + pkt_length * 4 + sel->buf_offset < sel->buf_offset) { + ret = EINVAL; + goto error; + } + + if ((pkt_length * 4 + sel->buf_offset) > sel->buf_len) { + ret = EINVAL; + goto error; + } + + memcpy(sel->tmp_buf + sel->buf_offset, shd_text, pkt_length * 4); + + sel->buf_offset += pkt_length * 4; + if (sel->buf_offset >= sel->buf_len) { + finished = true; + shd_text = sel->tmp_buf; + } + } + + if (finished) { + struct tgsi_token *tokens; + + /* check for null termination */ + uint32_t last_chunk_offset = sel->buf_offset ? sel->buf_offset : pkt_length * 4; + if (last_chunk_offset < 4 || !memchr(shd_text + last_chunk_offset - 4, '\0', 4)) { + ret = EINVAL; + goto error; + } + + tokens = calloc(num_tokens + 10, sizeof(struct tgsi_token)); + if (!tokens) { + ret = ENOMEM; + goto error; + } + + if (!tgsi_text_translate((const char *)shd_text, tokens, num_tokens + 10)) { + free(tokens); + ret = EINVAL; + goto error; + } + + if (vrend_finish_shader(ctx, sel, tokens)) { + free(tokens); + ret = EINVAL; + goto error; + } else { + free(sel->tmp_buf); + sel->tmp_buf = NULL; + } + free(tokens); + ctx->sub->long_shader_in_progress_handle[type] = 0; + } + + if (new_shader) { + ret_handle = vrend_renderer_object_insert(ctx, sel, sizeof(*sel), handle, VIRGL_OBJECT_SHADER); + if (ret_handle == 0) { + ret = ENOMEM; + goto error; + } + } + + return 0; + +error: + if (new_shader) + vrend_destroy_shader_selector(sel); + else + vrend_renderer_object_destroy(ctx, handle); + + return ret; +} + +void vrend_bind_shader(struct vrend_context *ctx, + uint32_t handle, uint32_t type) +{ + struct vrend_shader_selector *sel; + + if (type > PIPE_SHADER_COMPUTE) + return; + + if (handle == 0) { + if (type == PIPE_SHADER_COMPUTE) + ctx->sub->cs_shader_dirty = true; + else + ctx->sub->shader_dirty = true; + vrend_shader_state_reference(&ctx->sub->shaders[type], NULL); + return; + } + + sel = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_SHADER); + if (!sel) + return; + + if (sel->type != type) + return; + + if (ctx->sub->shaders[sel->type] != sel) { + if (type == PIPE_SHADER_COMPUTE) + ctx->sub->cs_shader_dirty = true; + else + ctx->sub->shader_dirty = true; + ctx->sub->prog_ids[sel->type] = 0; + } + + vrend_shader_state_reference(&ctx->sub->shaders[sel->type], sel); +} + +void vrend_clear(struct vrend_context *ctx, + unsigned buffers, + const union pipe_color_union *color, + double depth, unsigned stencil) +{ + GLbitfield bits = 0; + + if (ctx->in_error) + return; + + if (ctx->ctx_switch_pending) + vrend_finish_context_switch(ctx); + + vrend_update_frontface_state(ctx); + if (ctx->sub->stencil_state_dirty) + vrend_update_stencil_state(ctx); + if (ctx->sub->scissor_state_dirty) + vrend_update_scissor_state(ctx); + if (ctx->sub->viewport_state_dirty) + vrend_update_viewport_state(ctx); + + vrend_use_program(ctx, 0); + + glDisable(GL_SCISSOR_TEST); + + if (buffers & PIPE_CLEAR_COLOR) { + glClearColor(color->f[0], color->f[1], color->f[2], color->f[3]); + + /* This function implements Gallium's full clear callback (st->pipe->clear) on the host. This + callback requires no color component be masked. We must unmask all components before + calling glClear* and restore the previous colormask afterwards, as Gallium expects. */ + if (ctx->sub->hw_blend_state.independent_blend_enable && + has_feature(feat_indep_blend)) { + int i; + for (i = 0; i < PIPE_MAX_COLOR_BUFS; i++) + glColorMaski(i, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } else + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + + if (buffers & PIPE_CLEAR_DEPTH) { + /* gallium clears don't respect depth mask */ + glDepthMask(GL_TRUE); + glClearDepthf(depth); + } + + if (buffers & PIPE_CLEAR_STENCIL) { + glStencilMask(~0u); + glClearStencil(stencil); + } + + if (ctx->sub->hw_rs_state.rasterizer_discard) + glDisable(GL_RASTERIZER_DISCARD); + + if (buffers & PIPE_CLEAR_COLOR) { + uint32_t mask = 0; + int i; + for (i = 0; i < ctx->sub->nr_cbufs; i++) { + if (ctx->sub->surf[i]) + mask |= (1 << i); + } + if (mask != (buffers >> 2)) { + mask = buffers >> 2; + while (mask) { + i = u_bit_scan(&mask); + if (i < PIPE_MAX_COLOR_BUFS && ctx->sub->surf[i] && util_format_is_pure_uint(ctx->sub->surf[i] && ctx->sub->surf[i]->format)) + glClearBufferuiv(GL_COLOR, + i, (GLuint *)color); + else if (i < PIPE_MAX_COLOR_BUFS && ctx->sub->surf[i] && util_format_is_pure_sint(ctx->sub->surf[i] && ctx->sub->surf[i]->format)) + glClearBufferiv(GL_COLOR, + i, (GLint *)color); + else + glClearBufferfv(GL_COLOR, + i, (GLfloat *)color); + } + } + else + bits |= GL_COLOR_BUFFER_BIT; + } + if (buffers & PIPE_CLEAR_DEPTH) + bits |= GL_DEPTH_BUFFER_BIT; + if (buffers & PIPE_CLEAR_STENCIL) + bits |= GL_STENCIL_BUFFER_BIT; + + if (bits) + glClear(bits); + + /* Is it really necessary to restore the old states? The only reason we + * get here is because the guest cleared all those states but gallium + * didn't forward them before calling the clear command + */ + if (ctx->sub->hw_rs_state.rasterizer_discard) + glEnable(GL_RASTERIZER_DISCARD); + + if (buffers & PIPE_CLEAR_DEPTH) { + if (!ctx->sub->dsa_state.depth.writemask) + glDepthMask(GL_FALSE); + } + + /* Restore previous stencil buffer write masks for both front and back faces */ + if (buffers & PIPE_CLEAR_STENCIL) { + glStencilMaskSeparate(GL_FRONT, ctx->sub->dsa_state.stencil[0].writemask); + glStencilMaskSeparate(GL_BACK, ctx->sub->dsa_state.stencil[1].writemask); + } + + /* Restore previous colormask */ + if (buffers & PIPE_CLEAR_COLOR) { + glColorMask(ctx->sub->hw_blend_state.rt[0].colormask & PIPE_MASK_R ? GL_TRUE : GL_FALSE, + ctx->sub->hw_blend_state.rt[0].colormask & PIPE_MASK_G ? GL_TRUE : GL_FALSE, + ctx->sub->hw_blend_state.rt[0].colormask & PIPE_MASK_B ? GL_TRUE : GL_FALSE, + ctx->sub->hw_blend_state.rt[0].colormask & PIPE_MASK_A ? GL_TRUE : GL_FALSE); + } + if (ctx->sub->hw_rs_state.scissor) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); +} + +static void vrend_update_scissor_state(struct vrend_context *ctx) +{ + struct pipe_scissor_state *ss; + GLint y; + GLuint idx; + unsigned mask = ctx->sub->scissor_state_dirty; + + while (mask) { + idx = u_bit_scan(&mask); + if (idx >= PIPE_MAX_VIEWPORTS) + break; + ss = &ctx->sub->ss[idx]; + y = ss->miny; + + glScissor(ss->minx, y, ss->maxx - ss->minx, ss->maxy - ss->miny); + } + ctx->sub->scissor_state_dirty = 0; +} + +static void vrend_update_viewport_state(struct vrend_context *ctx) +{ + GLint cy; + unsigned mask = ctx->sub->viewport_state_dirty; + int idx; + while (mask) { + idx = u_bit_scan(&mask); + + if (ctx->sub->viewport_is_negative) + cy = ctx->sub->vps[idx].cur_y - ctx->sub->vps[idx].height; + else + cy = ctx->sub->vps[idx].cur_y; + + glViewport(ctx->sub->vps[idx].cur_x, cy, ctx->sub->vps[idx].width, ctx->sub->vps[idx].height); + glDepthRangef(ctx->sub->vps[idx].near_val, ctx->sub->vps[idx].far_val); + } + + ctx->sub->viewport_state_dirty = 0; +} + +static GLenum get_gs_xfb_mode(GLenum mode) +{ + switch (mode) { + case GL_POINTS: + return GL_POINTS; + case GL_LINE_STRIP: + return GL_LINES; + case GL_TRIANGLE_STRIP: + return GL_TRIANGLES; + default: + return GL_POINTS; + } +} + +static GLenum get_tess_xfb_mode(int mode, bool is_point_mode) +{ + if (is_point_mode) + return GL_POINTS; + switch (mode) { + case GL_QUADS: + case GL_TRIANGLES: + return GL_TRIANGLES; + case GL_LINES: + return GL_LINES; + default: + return GL_POINTS; + } +} + +static GLenum get_xfb_mode(GLenum mode) +{ + switch (mode) { + case GL_POINTS: + return GL_POINTS; + case GL_TRIANGLES: + case GL_TRIANGLE_STRIP: + case GL_TRIANGLE_FAN: + case GL_QUADS: + case GL_QUAD_STRIP: + case GL_POLYGON: + return GL_TRIANGLES; + case GL_LINES: + case GL_LINE_LOOP: + case GL_LINE_STRIP: + return GL_LINES; + default: + return GL_POINTS; + } +} + +static void vrend_draw_bind_vertex_legacy(struct vrend_context *ctx, + struct vrend_vertex_element_array *va) +{ + uint32_t enable_bitmask; + uint32_t disable_bitmask; + int i; + + enable_bitmask = 0; + disable_bitmask = ~((1ull << va->count) - 1); + for (i = 0; i < (int)va->count; i++) { + struct vrend_vertex_element *ve = &va->elements[i]; + int vbo_index = ve->base.vertex_buffer_index; + struct vrend_resource *res; + GLint loc; + + if (i >= ctx->sub->prog->ss[PIPE_SHADER_VERTEX]->sel->sinfo.num_inputs) + break; + res = (struct vrend_resource *)ctx->sub->vbo[vbo_index].buffer; + + if (!res) + continue; + + if (ctx->client->vrend_state->use_explicit_locations || has_feature(feat_gles31_vertex_attrib_binding)) { + loc = i; + } else { + if (ctx->sub->prog->attrib_locs) { + loc = ctx->sub->prog->attrib_locs[i]; + } else loc = -1; + + if (loc == -1) { + if (i == 0) + return; + continue; + } + } + + if (ve->type == GL_FALSE) + return; + + glBindBuffer(GL_ARRAY_BUFFER, res->id); + + if (ctx->sub->vbo[vbo_index].stride == 0) { + void *data; + /* for 0 stride we are kinda screwed */ + data = glMapBufferRange(GL_ARRAY_BUFFER, ctx->sub->vbo[vbo_index].buffer_offset, ve->nr_chan * sizeof(GLfloat), GL_MAP_READ_BIT); + + switch (ve->nr_chan) { + case 1: + glVertexAttrib1fv(loc, data); + break; + case 2: + glVertexAttrib2fv(loc, data); + break; + case 3: + glVertexAttrib3fv(loc, data); + break; + case 4: + default: + glVertexAttrib4fv(loc, data); + break; + } + glUnmapBuffer(GL_ARRAY_BUFFER); + disable_bitmask |= (1 << loc); + } else { + enable_bitmask |= (1 << loc); + if (util_format_is_pure_integer(ve->base.src_format)) { + glVertexAttribIPointer(loc, ve->nr_chan, ve->type, ctx->sub->vbo[vbo_index].stride, (void *)(unsigned long)(ve->base.src_offset + ctx->sub->vbo[vbo_index].buffer_offset)); + } else { + glVertexAttribPointer(loc, ve->nr_chan, ve->type, ve->norm, ctx->sub->vbo[vbo_index].stride, (void *)(unsigned long)(ve->base.src_offset + ctx->sub->vbo[vbo_index].buffer_offset)); + } + glVertexAttribDivisor(loc, ve->base.instance_divisor); + } + } + if (ctx->sub->enabled_attribs_bitmask != enable_bitmask) { + uint32_t mask = ctx->sub->enabled_attribs_bitmask & disable_bitmask; + + while (mask) { + i = u_bit_scan(&mask); + glDisableVertexAttribArray(i); + } + ctx->sub->enabled_attribs_bitmask &= ~disable_bitmask; + + mask = ctx->sub->enabled_attribs_bitmask ^ enable_bitmask; + while (mask) { + i = u_bit_scan(&mask); + glEnableVertexAttribArray(i); + } + + ctx->sub->enabled_attribs_bitmask = enable_bitmask; + } +} + +static void vrend_draw_bind_vertex_binding(struct vrend_context *ctx, + struct vrend_vertex_element_array *va) +{ + int i; + + glBindVertexArray(va->id); + + if (ctx->sub->vbo_dirty) { + GLsizei count = 0; + GLuint buffers[PIPE_MAX_ATTRIBS]; + GLintptr offsets[PIPE_MAX_ATTRIBS]; + GLsizei strides[PIPE_MAX_ATTRIBS]; + + for (i = 0; i < ctx->sub->num_vbos; i++) { + struct vrend_resource *res = (struct vrend_resource *)ctx->sub->vbo[i].buffer; + if (!res) { + buffers[count] = 0; + offsets[count] = 0; + strides[count++] = 0; + } else { + buffers[count] = res->id; + offsets[count] = ctx->sub->vbo[i].buffer_offset, + strides[count++] = ctx->sub->vbo[i].stride; + } + } + for (i = ctx->sub->num_vbos; i < ctx->sub->old_num_vbos; i++) { + buffers[count] = 0; + offsets[count] = 0; + strides[count++] = 0; + } + + for (i = 0; i < count; ++i) + glBindVertexBuffer(i, buffers[i], offsets[i], strides[i]); + + ctx->sub->vbo_dirty = false; + } +} + +static int vrend_draw_bind_samplers_shader(struct vrend_context *ctx, + int shader_type, + int next_sampler_id) +{ + int index = 0; + + uint32_t dirty = ctx->sub->sampler_views_dirty[shader_type]; + + uint32_t mask = ctx->sub->prog->samplers_used_mask[shader_type]; + while (mask) { + int i = u_bit_scan(&mask); + + struct vrend_sampler_view *tview = ctx->sub->views[shader_type].views[i]; + if (dirty & (1 << i) && tview) { + if (ctx->sub->prog->shadow_samp_mask[shader_type] & (1 << i)) { + glUniform4f(ctx->sub->prog->shadow_samp_mask_locs[shader_type][index], + (tview->gl_swizzle_r == GL_ZERO || tview->gl_swizzle_r == GL_ONE) ? 0.0 : 1.0, + (tview->gl_swizzle_g == GL_ZERO || tview->gl_swizzle_g == GL_ONE) ? 0.0 : 1.0, + (tview->gl_swizzle_b == GL_ZERO || tview->gl_swizzle_b == GL_ONE) ? 0.0 : 1.0, + (tview->gl_swizzle_a == GL_ZERO || tview->gl_swizzle_a == GL_ONE) ? 0.0 : 1.0); + glUniform4f(ctx->sub->prog->shadow_samp_add_locs[shader_type][index], + tview->gl_swizzle_r == GL_ONE ? 1.0 : 0.0, + tview->gl_swizzle_g == GL_ONE ? 1.0 : 0.0, + tview->gl_swizzle_b == GL_ONE ? 1.0 : 0.0, + tview->gl_swizzle_a == GL_ONE ? 1.0 : 0.0); + } + + if (tview->texture) { + GLuint id; + struct vrend_resource *texture = tview->texture; + GLenum target = tview->target; + + if (has_bit(tview->texture->storage_bits, VREND_STORAGE_GL_BUFFER)) { + id = texture->tbo_tex_id; + target = GL_TEXTURE_BUFFER; + } else + id = tview->id; + + glActiveTexture(GL_TEXTURE0 + next_sampler_id); + glBindTexture(target, id); + + if (ctx->sub->views[shader_type].old_ids[i] != id || + ctx->sub->sampler_views_dirty[shader_type] & (1 << i)) { + vrend_apply_sampler_state(ctx, texture, shader_type, i, + next_sampler_id, tview); + ctx->sub->views[shader_type].old_ids[i] = id; + } + dirty &= ~(1 << i); + } + } + next_sampler_id++; + index++; + } + ctx->sub->sampler_views_dirty[shader_type] = dirty; + + return next_sampler_id; +} + +static int vrend_draw_bind_ubo_shader(struct vrend_context *ctx, + int shader_type, int next_ubo_id) +{ + uint32_t mask, dirty, update; + struct pipe_constant_buffer *cb; + struct vrend_resource *res; + + if (!has_feature(feat_ubo)) + return next_ubo_id; + + mask = ctx->sub->prog->ubo_used_mask[shader_type]; + dirty = ctx->sub->const_bufs_dirty[shader_type]; + update = dirty & ctx->sub->const_bufs_used_mask[shader_type]; + + if (!update) + return next_ubo_id + util_bitcount(mask); + + while (mask) { + /* The const_bufs_used_mask stores the gallium uniform buffer indices */ + int i = u_bit_scan(&mask); + + if (update & (1 << i)) { + /* The cbs array is indexed using the gallium uniform buffer index */ + cb = &ctx->sub->cbs[shader_type][i]; + res = (struct vrend_resource *)cb->buffer; + + glBindBufferRange(GL_UNIFORM_BUFFER, next_ubo_id, res->id, + cb->buffer_offset, cb->buffer_size); + dirty &= ~(1 << i); + } + next_ubo_id++; + } + ctx->sub->const_bufs_dirty[shader_type] = dirty; + + return next_ubo_id; +} + +static void vrend_draw_bind_const_shader(struct vrend_context *ctx, + int shader_type, bool new_program) +{ + if (ctx->sub->consts[shader_type].consts && + ctx->sub->shaders[shader_type] && + (ctx->sub->prog->const_location[shader_type] != -1) && + (ctx->sub->const_dirty[shader_type] || new_program)) { + glUniform4uiv(ctx->sub->prog->const_location[shader_type], + ctx->sub->shaders[shader_type]->sinfo.num_consts, + ctx->sub->consts[shader_type].consts); + ctx->sub->const_dirty[shader_type] = false; + } +} + +static void vrend_draw_bind_ssbo_shader(struct vrend_context *ctx, int shader_type) +{ + uint32_t mask; + struct vrend_ssbo *ssbo; + struct vrend_resource *res; + int i; + + if (!has_feature(feat_ssbo)) + return; + + if (!ctx->sub->prog->ssbo_locs[shader_type]) + return; + + if (!ctx->sub->ssbo_used_mask[shader_type]) + return; + + mask = ctx->sub->ssbo_used_mask[shader_type]; + while (mask) { + i = u_bit_scan(&mask); + + ssbo = &ctx->sub->ssbo[shader_type][i]; + res = (struct vrend_resource *)ssbo->res; + glBindBufferRange(GL_SHADER_STORAGE_BUFFER, i, res->id, + ssbo->buffer_offset, ssbo->buffer_size); + } +} + +static void vrend_draw_bind_abo_shader(struct vrend_context *ctx) +{ + uint32_t mask; + struct vrend_abo *abo; + struct vrend_resource *res; + int i; + + if (!has_feature(feat_atomic_counters)) + return; + + mask = ctx->sub->abo_used_mask; + while (mask) { + i = u_bit_scan(&mask); + + abo = &ctx->sub->abo[i]; + res = (struct vrend_resource *)abo->res; + glBindBufferRange(GL_ATOMIC_COUNTER_BUFFER, i, res->id, + abo->buffer_offset, abo->buffer_size); + } +} + +static void vrend_draw_bind_images_shader(struct vrend_context *ctx, int shader_type) +{ + GLenum access; + GLboolean layered; + struct vrend_image_view *iview; + uint32_t mask, tex_id, level, first_layer; + + + if (!ctx->sub->images_used_mask[shader_type]) + return; + + if (!ctx->sub->prog->img_locs[shader_type]) + return; + + if (!has_feature(feat_images)) + return; + + mask = ctx->sub->images_used_mask[shader_type]; + while (mask) { + unsigned i = u_bit_scan(&mask); + + if (!(ctx->sub->prog->images_used_mask[shader_type] & (1 << i))) + continue; + iview = &ctx->sub->image_views[shader_type][i]; + tex_id = iview->texture->id; + if (has_bit(iview->texture->storage_bits, VREND_STORAGE_GL_BUFFER)) { + if (!iview->texture->tbo_tex_id) + glGenTextures(1, &iview->texture->tbo_tex_id); + + /* glTexBuffer doesn't accept GL_RGBA8_SNORM, find an appropriate replacement. */ + uint32_t format = (iview->format == GL_RGBA8_SNORM) ? GL_RGBA8UI : iview->format; + + glBindBuffer(GL_TEXTURE_BUFFER, iview->texture->id); + glBindTexture(GL_TEXTURE_BUFFER, iview->texture->tbo_tex_id); + + if (has_feature(feat_arb_or_gles_ext_texture_buffer)) + glTexBuffer(GL_TEXTURE_BUFFER, format, iview->texture->id); + + tex_id = iview->texture->tbo_tex_id; + level = first_layer = 0; + layered = GL_TRUE; + } else { + level = iview->u.tex.level; + first_layer = iview->u.tex.first_layer; + layered = !((iview->texture->base.array_size > 1 || + iview->texture->base.depth0 > 1) && (iview->u.tex.first_layer == iview->u.tex.last_layer)); + } + + switch (iview->access) { + case PIPE_IMAGE_ACCESS_READ: + access = GL_READ_ONLY; + break; + case PIPE_IMAGE_ACCESS_WRITE: + access = GL_WRITE_ONLY; + break; + case PIPE_IMAGE_ACCESS_READ_WRITE: + access = GL_READ_WRITE; + break; + default: + return; + } + + glBindImageTexture(i, tex_id, level, layered, first_layer, access, iview->format); + } +} + +static void vrend_draw_bind_objects(struct vrend_context *ctx, bool new_program) +{ + int next_ubo_id = 0, next_sampler_id = 0; + for (int shader_type = PIPE_SHADER_VERTEX; shader_type <= ctx->sub->last_shader_idx; shader_type++) { + next_ubo_id = vrend_draw_bind_ubo_shader(ctx, shader_type, next_ubo_id); + vrend_draw_bind_const_shader(ctx, shader_type, new_program); + next_sampler_id = vrend_draw_bind_samplers_shader(ctx, shader_type, + next_sampler_id); + vrend_draw_bind_images_shader(ctx, shader_type); + vrend_draw_bind_ssbo_shader(ctx, shader_type); + } + + vrend_draw_bind_abo_shader(ctx); + + if (ctx->sub->prog->fs_stipple_loc != -1) { + glActiveTexture(GL_TEXTURE0 + next_sampler_id); + glBindTexture(GL_TEXTURE_2D, ctx->pstipple_tex_id); + glUniform1i(ctx->sub->prog->fs_stipple_loc, next_sampler_id); + } +} + +static +void vrend_inject_tcs(struct vrend_context *ctx, int vertices_per_patch) +{ + struct pipe_stream_output_info so_info; + + memset(&so_info, 0, sizeof(so_info)); + struct vrend_shader_selector *sel = vrend_create_shader_state(ctx, + &so_info, + false, PIPE_SHADER_TESS_CTRL); + struct vrend_shader *shader; + shader = CALLOC_STRUCT(vrend_shader); + vrend_fill_shader_key(ctx, sel, &shader->key); + + shader->sel = sel; + list_inithead(&shader->programs); + strarray_alloc(&shader->glsl_strings, SHADER_MAX_STRINGS); + + vrend_shader_create_passthrough_tcs(ctx, &ctx->shader_cfg, + ctx->sub->shaders[PIPE_SHADER_VERTEX]->tokens, + &shader->key, ctx->client->vrend_state->tess_factors, &sel->sinfo, + &shader->glsl_strings, vertices_per_patch); + // Need to add inject the selected shader to the shader selector and then the code below + // can continue + sel->tokens = NULL; + sel->current = shader; + ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] = sel; + ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->num_shaders = 1; + + shader->id = glCreateShader(conv_shader_type(shader->sel->type)); + vrend_compile_shader(ctx, shader); +} + +int vrend_draw_vbo(struct vrend_context *ctx, + const struct pipe_draw_info *info, + uint32_t cso, uint32_t indirect_handle, + uint32_t indirect_draw_count_handle) +{ + int i; + bool new_program = false; + struct vrend_resource *indirect_res = NULL; + + if (ctx->in_error) + return 0; + + if (info->instance_count && !has_feature(feat_draw_instance)) + return EINVAL; + + if (info->start_instance || info->indirect.draw_count > 1) + return EINVAL; + + if (indirect_handle) { + if (!has_feature(feat_indirect_draw)) + return EINVAL; + + indirect_res = vrend_renderer_ctx_res_lookup(ctx, indirect_handle); + if (!indirect_res) + return 0; + } + + if (indirect_draw_count_handle) + return EINVAL; + + if (ctx->ctx_switch_pending) + vrend_finish_context_switch(ctx); + + vrend_update_frontface_state(ctx); + if (ctx->sub->stencil_state_dirty) + vrend_update_stencil_state(ctx); + if (ctx->sub->scissor_state_dirty) + vrend_update_scissor_state(ctx); + + if (ctx->sub->viewport_state_dirty) + vrend_update_viewport_state(ctx); + + if (ctx->sub->blend_state_dirty) + vrend_patch_blend_state(ctx); + + if (ctx->sub->shader_dirty || ctx->sub->swizzle_output_rgb_to_bgr) { + struct vrend_linked_shader_program *prog; + bool fs_dirty, vs_dirty, gs_dirty, tcs_dirty, tes_dirty; + bool dual_src = util_blend_state_is_dual(&ctx->sub->blend_state, 0); + bool same_prog; + + ctx->sub->shader_dirty = false; + + if (!ctx->sub->shaders[PIPE_SHADER_VERTEX] || !ctx->sub->shaders[PIPE_SHADER_FRAGMENT]) + return 0; + + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_VERTEX], &vs_dirty); + + if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] && ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->tokens) + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_TESS_CTRL], &tcs_dirty); + else if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) { + vrend_inject_tcs(ctx, info->vertices_per_patch); + + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_VERTEX], &vs_dirty); + } + + if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_TESS_EVAL], &tes_dirty); + if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY]) + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_GEOMETRY], &gs_dirty); + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_FRAGMENT], &fs_dirty); + + if (!ctx->sub->shaders[PIPE_SHADER_VERTEX]->current || + !ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current || + (ctx->sub->shaders[PIPE_SHADER_GEOMETRY] && !ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current) || + (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] && !ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current) || + (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] && !ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current)) { + return 0; + } + same_prog = true; + if (ctx->sub->shaders[PIPE_SHADER_VERTEX]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_VERTEX]) + same_prog = false; + if (ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_FRAGMENT]) + same_prog = false; + if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY] && ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_GEOMETRY]) + same_prog = false; + if (ctx->sub->prog && ctx->sub->prog->dual_src_linked != dual_src) + same_prog = false; + if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] && ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_TESS_CTRL]) + same_prog = false; + if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] && ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_TESS_EVAL]) + same_prog = false; + + if (!same_prog) { + prog = lookup_shader_program(ctx, + ctx->sub->shaders[PIPE_SHADER_VERTEX]->current->id, + ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current->id, + ctx->sub->shaders[PIPE_SHADER_GEOMETRY] ? ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current->id : 0, + ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] ? ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current->id : 0, + ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] ? ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current->id : 0, + dual_src); + if (!prog) { + prog = add_shader_program(ctx, + ctx->sub->shaders[PIPE_SHADER_VERTEX]->current, + ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current, + ctx->sub->shaders[PIPE_SHADER_GEOMETRY] ? ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current : NULL, + ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] ? ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current : NULL, + ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] ? ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current : NULL); + if (!prog) + return 0; + } + + ctx->sub->last_shader_idx = ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] ? PIPE_SHADER_TESS_EVAL : (ctx->sub->shaders[PIPE_SHADER_GEOMETRY] ? PIPE_SHADER_GEOMETRY : PIPE_SHADER_FRAGMENT); + } else + prog = ctx->sub->prog; + if (ctx->sub->prog != prog) { + new_program = true; + ctx->sub->prog_ids[PIPE_SHADER_VERTEX] = ctx->sub->shaders[PIPE_SHADER_VERTEX]->current->id; + ctx->sub->prog_ids[PIPE_SHADER_FRAGMENT] = ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current->id; + if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY]) + ctx->sub->prog_ids[PIPE_SHADER_GEOMETRY] = ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current->id; + if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]) + ctx->sub->prog_ids[PIPE_SHADER_TESS_CTRL] = ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current->id; + if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) + ctx->sub->prog_ids[PIPE_SHADER_TESS_EVAL] = ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current->id; + ctx->sub->prog_ids[PIPE_SHADER_COMPUTE] = -1; + ctx->sub->prog = prog; + + /* mark all constbufs and sampler views as dirty */ + for (int stage = PIPE_SHADER_VERTEX; stage <= PIPE_SHADER_FRAGMENT; stage++) { + ctx->sub->const_bufs_dirty[stage] = ~0; + ctx->sub->sampler_views_dirty[stage] = ~0; + } + + prog->ref_context = ctx->sub; + } + } + if (!ctx->sub->prog) + return 0; + + vrend_use_program(ctx, ctx->sub->prog->id); + + vrend_draw_bind_objects(ctx, new_program); + + if (!ctx->sub->ve) + return 0; + float viewport_neg_val = ctx->sub->viewport_is_negative ? -1.0 : 1.0; + if (ctx->sub->prog->viewport_neg_val != viewport_neg_val) { + glUniform1f(ctx->sub->prog->vs_ws_adjust_loc, viewport_neg_val); + ctx->sub->prog->viewport_neg_val = viewport_neg_val; + } + + if (ctx->sub->rs_state.clip_plane_enable) { + for (i = 0 ; i < 8; i++) { + glUniform4fv(ctx->sub->prog->clip_locs[i], 1, (const GLfloat *)&ctx->sub->ucp_state.ucp[i]); + } + } + + if (has_feature(feat_gles31_vertex_attrib_binding)) + vrend_draw_bind_vertex_binding(ctx, ctx->sub->ve); + else + vrend_draw_bind_vertex_legacy(ctx, ctx->sub->ve); + + for (i = 0 ; i < ctx->sub->prog->ss[PIPE_SHADER_VERTEX]->sel->sinfo.num_inputs; i++) { + struct vrend_vertex_element_array *va = ctx->sub->ve; + struct vrend_vertex_element *ve = &va->elements[i]; + int vbo_index = ve->base.vertex_buffer_index; + if (!ctx->sub->vbo[vbo_index].buffer) + return 0; + } + + if (info->indexed) { + struct vrend_resource *res = (struct vrend_resource *)ctx->sub->ib.buffer; + if (!res) + return 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, res->id); + } else + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + if (ctx->sub->current_so) { + if (ctx->sub->current_so->xfb_state == XFB_STATE_STARTED_NEED_BEGIN) { + if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY]) + glBeginTransformFeedback(get_gs_xfb_mode(ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->sinfo.gs_out_prim)); + else if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]) + glBeginTransformFeedback(get_tess_xfb_mode(ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_prim, + ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_point_mode)); + else + glBeginTransformFeedback(get_xfb_mode(info->mode)); + ctx->sub->current_so->xfb_state = XFB_STATE_STARTED; + } else if (ctx->sub->current_so->xfb_state == XFB_STATE_PAUSED) { + glResumeTransformFeedback(); + ctx->sub->current_so->xfb_state = XFB_STATE_STARTED; + } + } + + if (info->primitive_restart) { + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + + if (has_feature(feat_indirect_draw)) { + GLint buf = indirect_res ? indirect_res->id : 0; + if (ctx->sub->draw_indirect_buffer != buf) { + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buf); + ctx->sub->draw_indirect_buffer = buf; + } + } + + if (info->vertices_per_patch && has_feature(feat_tessellation)) + glPatchParameteri(GL_PATCH_VERTICES, info->vertices_per_patch); + + /* set the vertex state up now on a delay */ + if (!info->indexed) { + GLenum mode = info->mode; + int count = cso ? cso : info->count; + int start = cso ? 0 : info->start; + + if (indirect_handle) { + glDrawArraysIndirect(mode, (GLvoid const *)(unsigned long)info->indirect.offset); + } else if (info->instance_count <= 1) + glDrawArrays(mode, start, count); + else + glDrawArraysInstanced(mode, start, count, info->instance_count); + } else { + GLenum elsz; + GLenum mode = info->mode; + switch (ctx->sub->ib.index_size) { + case 1: + elsz = GL_UNSIGNED_BYTE; + break; + case 2: + elsz = GL_UNSIGNED_SHORT; + break; + case 4: + default: + elsz = GL_UNSIGNED_INT; + break; + } + + if (indirect_handle) { + glDrawElementsIndirect(mode, elsz, (GLvoid const *)(unsigned long)info->indirect.offset); + } else if (info->index_bias) { + if (info->instance_count > 1) + glDrawElementsInstancedBaseVertex(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset, info->instance_count, info->index_bias); + else if (info->min_index != 0 || info->max_index != (unsigned)-1) + glDrawRangeElementsBaseVertex(mode, info->min_index, info->max_index, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset, info->index_bias); + else + glDrawElementsBaseVertex(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset, info->index_bias); + } else if (info->instance_count > 1) { + glDrawElementsInstanced(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset, info->instance_count); + } else if (info->min_index != 0 || info->max_index != (unsigned)-1) + glDrawRangeElements(mode, info->min_index, info->max_index, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset); + else + glDrawElements(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset); + } + + if (info->primitive_restart) { + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + + if (ctx->sub->current_so && has_feature(feat_transform_feedback2)) { + if (ctx->sub->current_so->xfb_state == XFB_STATE_STARTED) { + glPauseTransformFeedback(); + ctx->sub->current_so->xfb_state = XFB_STATE_PAUSED; + } + } + return 0; +} + +void vrend_launch_grid(struct vrend_context *ctx, + UNUSED uint32_t *block, + uint32_t *grid, + uint32_t indirect_handle, + uint32_t indirect_offset) +{ + bool new_program = false; + struct vrend_resource *indirect_res = NULL; + + if (!has_feature(feat_compute_shader)) + return; + + if (ctx->sub->cs_shader_dirty) { + struct vrend_linked_shader_program *prog; + bool cs_dirty; + + ctx->sub->cs_shader_dirty = false; + + if (!ctx->sub->shaders[PIPE_SHADER_COMPUTE]) + return; + + vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_COMPUTE], &cs_dirty); + if (!ctx->sub->shaders[PIPE_SHADER_COMPUTE]->current) + return; + if (ctx->sub->shaders[PIPE_SHADER_COMPUTE]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_COMPUTE]) { + prog = lookup_cs_shader_program(ctx, ctx->sub->shaders[PIPE_SHADER_COMPUTE]->current->id); + if (!prog) { + prog = add_cs_shader_program(ctx, ctx->sub->shaders[PIPE_SHADER_COMPUTE]->current); + if (!prog) + return; + } + } else + prog = ctx->sub->prog; + + if (ctx->sub->prog != prog) { + new_program = true; + ctx->sub->prog_ids[PIPE_SHADER_VERTEX] = -1; + ctx->sub->prog_ids[PIPE_SHADER_COMPUTE] = ctx->sub->shaders[PIPE_SHADER_COMPUTE]->current->id; + ctx->sub->prog = prog; + prog->ref_context = ctx->sub; + } + ctx->sub->shader_dirty = true; + } + vrend_use_program(ctx, ctx->sub->prog->id); + + vrend_draw_bind_ubo_shader(ctx, PIPE_SHADER_COMPUTE, 0); + vrend_draw_bind_const_shader(ctx, PIPE_SHADER_COMPUTE, new_program); + vrend_draw_bind_samplers_shader(ctx, PIPE_SHADER_COMPUTE, 0); + vrend_draw_bind_images_shader(ctx, PIPE_SHADER_COMPUTE); + vrend_draw_bind_ssbo_shader(ctx, PIPE_SHADER_COMPUTE); + vrend_draw_bind_abo_shader(ctx); + + if (indirect_handle) { + indirect_res = vrend_renderer_ctx_res_lookup(ctx, indirect_handle); + if (!indirect_res) + return; + } + + if (indirect_res) + glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, indirect_res->id); + else + glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0); + + if (indirect_res) { + glDispatchComputeIndirect(indirect_offset); + } else { + glDispatchCompute(grid[0], grid[1], grid[2]); + } +} + +static GLenum translate_blend_func(uint32_t pipe_blend) +{ + switch(pipe_blend){ + case PIPE_BLEND_ADD: return GL_FUNC_ADD; + case PIPE_BLEND_SUBTRACT: return GL_FUNC_SUBTRACT; + case PIPE_BLEND_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + case PIPE_BLEND_MIN: return GL_MIN; + case PIPE_BLEND_MAX: return GL_MAX; + default: + assert("invalid blend token()" == NULL); + return 0; + } +} + +static GLenum translate_blend_factor(uint32_t pipe_factor) +{ + switch (pipe_factor) { + case PIPE_BLENDFACTOR_ONE: return GL_ONE; + case PIPE_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case PIPE_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + + case PIPE_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case PIPE_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + + case PIPE_BLENDFACTOR_CONST_COLOR: return GL_CONSTANT_COLOR; + case PIPE_BLENDFACTOR_CONST_ALPHA: return GL_CONSTANT_ALPHA; + + case PIPE_BLENDFACTOR_SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE; + case PIPE_BLENDFACTOR_ZERO: return GL_ZERO; + + case PIPE_BLENDFACTOR_INV_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case PIPE_BLENDFACTOR_INV_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + + case PIPE_BLENDFACTOR_INV_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case PIPE_BLENDFACTOR_INV_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + + case PIPE_BLENDFACTOR_INV_CONST_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case PIPE_BLENDFACTOR_INV_CONST_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + + default: + assert("invalid blend token()" == NULL); + return 0; + } +} + +static GLenum +translate_stencil_op(GLuint op) +{ + switch (op) { +#define CASE(x) case PIPE_STENCIL_OP_##x: return GL_##x + CASE(KEEP); + CASE(ZERO); + CASE(REPLACE); + CASE(INCR); + CASE(DECR); + CASE(INCR_WRAP); + CASE(DECR_WRAP); + CASE(INVERT); + default: + assert("invalid stencilop token()" == NULL); + return 0; + } +#undef CASE +} + +static inline bool is_dst_blend(int blend_factor) +{ + return (blend_factor == PIPE_BLENDFACTOR_DST_ALPHA || + blend_factor == PIPE_BLENDFACTOR_INV_DST_ALPHA); +} + +static inline int conv_dst_blend(int blend_factor) +{ + if (blend_factor == PIPE_BLENDFACTOR_DST_ALPHA) + return PIPE_BLENDFACTOR_ONE; + if (blend_factor == PIPE_BLENDFACTOR_INV_DST_ALPHA) + return PIPE_BLENDFACTOR_ZERO; + return blend_factor; +} + +static void vrend_hw_emit_blend(struct vrend_context *ctx, struct pipe_blend_state *state) +{ + if (state->logicop_enable != ctx->sub->hw_blend_state.logicop_enable) { + ctx->sub->hw_blend_state.logicop_enable = state->logicop_enable; + if (can_emulate_logicop(state->logicop_func)) + ctx->sub->shader_dirty = true; + } + + if (state->independent_blend_enable && + has_feature(feat_indep_blend) && + has_feature(feat_indep_blend_func)) { + /* ARB_draw_buffers_blend is required for this */ + int i; + + for (i = 0; i < PIPE_MAX_COLOR_BUFS; i++) { + if (state->rt[i].blend_enable) { + glBlendFuncSeparatei(i, translate_blend_factor(state->rt[i].rgb_src_factor), + translate_blend_factor(state->rt[i].rgb_dst_factor), + translate_blend_factor(state->rt[i].alpha_src_factor), + translate_blend_factor(state->rt[i].alpha_dst_factor)); + glBlendEquationSeparatei(i, translate_blend_func(state->rt[i].rgb_func), + translate_blend_func(state->rt[i].alpha_func)); + glEnablei(GL_BLEND, i); + } else { + glDisablei(GL_BLEND, i); + } + + if (state->rt[i].colormask != ctx->sub->hw_blend_state.rt[i].colormask) { + ctx->sub->hw_blend_state.rt[i].colormask = state->rt[i].colormask; + glColorMaski(i, state->rt[i].colormask & PIPE_MASK_R ? GL_TRUE : GL_FALSE, + state->rt[i].colormask & PIPE_MASK_G ? GL_TRUE : GL_FALSE, + state->rt[i].colormask & PIPE_MASK_B ? GL_TRUE : GL_FALSE, + state->rt[i].colormask & PIPE_MASK_A ? GL_TRUE : GL_FALSE); + } + } + } else { + if (state->rt[0].blend_enable) { + glBlendFuncSeparate(translate_blend_factor(state->rt[0].rgb_src_factor), + translate_blend_factor(state->rt[0].rgb_dst_factor), + translate_blend_factor(state->rt[0].alpha_src_factor), + translate_blend_factor(state->rt[0].alpha_dst_factor)); + glBlendEquationSeparate(translate_blend_func(state->rt[0].rgb_func), + translate_blend_func(state->rt[0].alpha_func)); + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } + + if (state->rt[0].colormask != ctx->sub->hw_blend_state.rt[0].colormask) { + int i; + for (i = 0; i < PIPE_MAX_COLOR_BUFS; i++) + ctx->sub->hw_blend_state.rt[i].colormask = state->rt[i].colormask; + glColorMask(state->rt[0].colormask & PIPE_MASK_R ? GL_TRUE : GL_FALSE, + state->rt[0].colormask & PIPE_MASK_G ? GL_TRUE : GL_FALSE, + state->rt[0].colormask & PIPE_MASK_B ? GL_TRUE : GL_FALSE, + state->rt[0].colormask & PIPE_MASK_A ? GL_TRUE : GL_FALSE); + } + } + ctx->sub->hw_blend_state.independent_blend_enable = state->independent_blend_enable; + + if (has_feature(feat_multisample)) { + if (state->alpha_to_coverage) + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + else + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + + if (state->dither) + glEnable(GL_DITHER); + else + glDisable(GL_DITHER); +} + +/* there are a few reasons we might need to patch the blend state. + a) patching blend factors for dst with no alpha + b) patching colormask/blendcolor/blendfactors for A8/A16 format + emulation using GL_R8/GL_R16. +*/ +static void vrend_patch_blend_state(struct vrend_context *ctx) +{ + struct pipe_blend_state new_state = ctx->sub->blend_state; + struct pipe_blend_state *state = &ctx->sub->blend_state; + bool swizzle_blend_color = false; + struct pipe_blend_color blend_color = ctx->sub->blend_color; + int i; + + if (ctx->sub->nr_cbufs == 0) { + ctx->sub->blend_state_dirty = false; + return; + } + + for (i = 0; i < (state->independent_blend_enable ? PIPE_MAX_COLOR_BUFS : 1); i++) { + if (i < ctx->sub->nr_cbufs && ctx->sub->surf[i]) { + if (!util_format_has_alpha(ctx->sub->surf[i]->format)) { + if (!(is_dst_blend(state->rt[i].rgb_src_factor) || + is_dst_blend(state->rt[i].rgb_dst_factor) || + is_dst_blend(state->rt[i].alpha_src_factor) || + is_dst_blend(state->rt[i].alpha_dst_factor))) + continue; + new_state.rt[i].rgb_src_factor = conv_dst_blend(state->rt[i].rgb_src_factor); + new_state.rt[i].rgb_dst_factor = conv_dst_blend(state->rt[i].rgb_dst_factor); + new_state.rt[i].alpha_src_factor = conv_dst_blend(state->rt[i].alpha_src_factor); + new_state.rt[i].alpha_dst_factor = conv_dst_blend(state->rt[i].alpha_dst_factor); + } + } + } + + vrend_hw_emit_blend(ctx, &new_state); + + if (swizzle_blend_color) { + blend_color.color[0] = blend_color.color[3]; + blend_color.color[1] = 0.0f; + blend_color.color[2] = 0.0f; + blend_color.color[3] = 0.0f; + } + + glBlendColor(blend_color.color[0], + blend_color.color[1], + blend_color.color[2], + blend_color.color[3]); + + ctx->sub->blend_state_dirty = false; +} + +void vrend_object_bind_blend(struct vrend_context *ctx, + uint32_t handle) +{ + struct pipe_blend_state *state; + + if (handle == 0) { + memset(&ctx->sub->blend_state, 0, sizeof(ctx->sub->blend_state)); + glDisable(GL_BLEND); + return; + } + state = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_BLEND); + if (!state) + return; + + ctx->sub->shader_dirty = true; + ctx->sub->blend_state = *state; + + ctx->sub->blend_state_dirty = true; +} + +static void vrend_hw_emit_dsa(struct vrend_context *ctx) +{ + struct pipe_depth_stencil_alpha_state *state = &ctx->sub->dsa_state; + + if (state->depth.enabled) { + vrend_depth_test_enable(ctx, true); + glDepthFunc(GL_NEVER + state->depth.func); + if (state->depth.writemask) + glDepthMask(GL_TRUE); + else + glDepthMask(GL_FALSE); + } else + vrend_depth_test_enable(ctx, false); + +} +void vrend_object_bind_dsa(struct vrend_context *ctx, + uint32_t handle) +{ + struct pipe_depth_stencil_alpha_state *state; + + if (handle == 0) { + memset(&ctx->sub->dsa_state, 0, sizeof(ctx->sub->dsa_state)); + ctx->sub->dsa = NULL; + ctx->sub->stencil_state_dirty = true; + ctx->sub->shader_dirty = true; + vrend_hw_emit_dsa(ctx); + return; + } + + state = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_DSA); + if (!state) + return; + + if (ctx->sub->dsa != state) { + ctx->sub->stencil_state_dirty = true; + ctx->sub->shader_dirty = true; + } + ctx->sub->dsa_state = *state; + ctx->sub->dsa = state; + + vrend_hw_emit_dsa(ctx); +} + +static void vrend_update_frontface_state(struct vrend_context *ctx) +{ + struct pipe_rasterizer_state *state = &ctx->sub->rs_state; + int front_ccw = state->front_ccw; + + front_ccw ^= (ctx->sub->inverted_fbo_content ? 0 : 1); + if (front_ccw) + glFrontFace(GL_CCW); + else + glFrontFace(GL_CW); +} + +void vrend_update_stencil_state(struct vrend_context *ctx) +{ + struct pipe_depth_stencil_alpha_state *state = ctx->sub->dsa; + int i; + if (!state) + return; + + if (!state->stencil[1].enabled) { + if (state->stencil[0].enabled) { + vrend_stencil_test_enable(ctx, true); + + glStencilOp(translate_stencil_op(state->stencil[0].fail_op), + translate_stencil_op(state->stencil[0].zfail_op), + translate_stencil_op(state->stencil[0].zpass_op)); + + glStencilFunc(GL_NEVER + state->stencil[0].func, + ctx->sub->stencil_refs[0], + state->stencil[0].valuemask); + glStencilMask(state->stencil[0].writemask); + } else + vrend_stencil_test_enable(ctx, false); + } else { + vrend_stencil_test_enable(ctx, true); + + for (i = 0; i < 2; i++) { + GLenum face = (i == 1) ? GL_BACK : GL_FRONT; + glStencilOpSeparate(face, + translate_stencil_op(state->stencil[i].fail_op), + translate_stencil_op(state->stencil[i].zfail_op), + translate_stencil_op(state->stencil[i].zpass_op)); + + glStencilFuncSeparate(face, GL_NEVER + state->stencil[i].func, + ctx->sub->stencil_refs[i], + state->stencil[i].valuemask); + glStencilMaskSeparate(face, state->stencil[i].writemask); + } + } + ctx->sub->stencil_state_dirty = false; +} + +static void vrend_hw_emit_rs(struct vrend_context *ctx) +{ + struct pipe_rasterizer_state *state = &ctx->sub->rs_state; + int i; + + /* line_width < 0 is invalid, the guest sometimes forgot to set it. */ + glLineWidth(state->line_width <= 0 ? 1.0f : state->line_width); + + if (state->rasterizer_discard != ctx->sub->hw_rs_state.rasterizer_discard) { + ctx->sub->hw_rs_state.rasterizer_discard = state->rasterizer_discard; + if (state->rasterizer_discard) + glEnable(GL_RASTERIZER_DISCARD); + else + glDisable(GL_RASTERIZER_DISCARD); + } + + if (state->offset_tri) { + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + + if (state->flatshade != ctx->sub->hw_rs_state.flatshade) { + ctx->sub->hw_rs_state.flatshade = state->flatshade; + } + + if (state->flatshade_first != ctx->sub->hw_rs_state.flatshade_first) { + ctx->sub->hw_rs_state.flatshade_first = state->flatshade_first; + } + + glPolygonOffset(state->offset_scale, state->offset_units); + + if (state->poly_stipple_enable && !ctx->pstip_inited) { + vrend_init_pstipple_texture(ctx); + } + + if (state->cull_face != PIPE_FACE_NONE) { + switch (state->cull_face) { + case PIPE_FACE_FRONT: + glCullFace(GL_FRONT); + break; + case PIPE_FACE_BACK: + glCullFace(GL_BACK); + break; + case PIPE_FACE_FRONT_AND_BACK: + glCullFace(GL_FRONT_AND_BACK); + break; + } + glEnable(GL_CULL_FACE); + } else + glDisable(GL_CULL_FACE); + + if (has_feature(feat_multisample)) { + if (has_feature(feat_sample_mask)) { + if (state->multisample) + glEnable(GL_SAMPLE_MASK); + else + glDisable(GL_SAMPLE_MASK); + } + + if (has_feature(feat_sample_shading)) { + if (state->force_persample_interp) + glEnable(GL_SAMPLE_SHADING); + else + glDisable(GL_SAMPLE_SHADING); + } + } + + if (state->scissor) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + ctx->sub->hw_rs_state.scissor = state->scissor; + +} + +void vrend_object_bind_rasterizer(struct vrend_context *ctx, + uint32_t handle) +{ + struct pipe_rasterizer_state *state; + + if (handle == 0) { + memset(&ctx->sub->rs_state, 0, sizeof(ctx->sub->rs_state)); + return; + } + + state = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_RASTERIZER); + + if (!state) + return; + + ctx->sub->rs_state = *state; + ctx->sub->shader_dirty = true; + vrend_hw_emit_rs(ctx); +} + +void vrend_bind_sampler_states(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t start_slot, + uint32_t num_states, + uint32_t *handles) +{ + uint32_t i; + struct vrend_sampler_state *state; + + if (shader_type >= PIPE_SHADER_TYPES) + return; + + if (num_states > PIPE_MAX_SAMPLERS || + start_slot > (PIPE_MAX_SAMPLERS - num_states)) + return; + + ctx->sub->num_sampler_states[shader_type] = num_states; + + uint32_t dirty = 0; + for (i = 0; i < num_states; i++) { + if (handles[i] == 0) + state = NULL; + else + state = vrend_object_lookup(ctx->sub->object_hash, handles[i], VIRGL_OBJECT_SAMPLER_STATE); + + ctx->sub->sampler_state[shader_type][i + start_slot] = state; + dirty |= 1 << (start_slot + i); + } + ctx->sub->sampler_views_dirty[shader_type] |= dirty; +} + +static bool get_swizzled_border_color(enum virgl_formats fmt, + union pipe_color_union *in_border_color, + union pipe_color_union *out_border_color) +{ + const struct vrend_format_table *fmt_entry = vrend_get_format_table_entry(fmt); + if ((fmt_entry->flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE) && + (fmt_entry->bindings & VIRGL_BIND_PREFER_EMULATED_BGRA)) { + for (int i = 0; i < 4; ++i) { + int swz = fmt_entry->swizzle[i]; + switch (swz) { + case PIPE_SWIZZLE_ZERO: out_border_color->ui[i] = 0; + break; + case PIPE_SWIZZLE_ONE: out_border_color->ui[i] = 1; + break; + default: + out_border_color->ui[i] = in_border_color->ui[swz]; + } + } + return true; + } + return false; +} + +static void vrend_apply_sampler_state(struct vrend_context *ctx, + struct vrend_resource *res, + uint32_t shader_type, + int id, + int sampler_id, + struct vrend_sampler_view *tview) +{ + struct vrend_texture *tex = (struct vrend_texture *)res; + struct vrend_sampler_state *vstate = ctx->sub->sampler_state[shader_type][id]; + struct pipe_sampler_state *state = &vstate->base; + bool set_all = false; + GLenum target = tex->base.target; + + if (!state) + return; + if (res->base.nr_samples > 0) { + tex->state = *state; + return; + } + + if (has_bit(tex->base.storage_bits, VREND_STORAGE_GL_BUFFER)) { + tex->state = *state; + return; + } + + if (has_feature(feat_samplers)) { + int sampler = vstate->ids[tview->srgb_decode == GL_SKIP_DECODE_EXT ? 0 : 1]; + union pipe_color_union border_color; + if (get_swizzled_border_color(tview->format, &state->border_color, &border_color)) + glSamplerParameterIuiv(sampler, GL_TEXTURE_BORDER_COLOR, border_color.ui); + + glBindSampler(sampler_id, sampler); + return; + } + + if (tex->state.max_lod == -1) + set_all = true; + + if (tex->state.wrap_s != state->wrap_s || set_all) + glTexParameteri(target, GL_TEXTURE_WRAP_S, convert_wrap(state->wrap_s)); + if (tex->state.wrap_t != state->wrap_t || set_all) + glTexParameteri(target, GL_TEXTURE_WRAP_T, convert_wrap(state->wrap_t)); + if (tex->state.wrap_r != state->wrap_r || set_all) + glTexParameteri(target, GL_TEXTURE_WRAP_R, convert_wrap(state->wrap_r)); + if (tex->state.min_img_filter != state->min_img_filter || + tex->state.min_mip_filter != state->min_mip_filter || set_all) + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, convert_min_filter(state->min_img_filter, state->min_mip_filter)); + if (tex->state.mag_img_filter != state->mag_img_filter || set_all) + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, convert_mag_filter(state->mag_img_filter)); + if (res->target != GL_TEXTURE_RECTANGLE) { + if (tex->state.min_lod != state->min_lod || set_all) + glTexParameterf(target, GL_TEXTURE_MIN_LOD, state->min_lod); + if (tex->state.max_lod != state->max_lod || set_all) + glTexParameterf(target, GL_TEXTURE_MAX_LOD, state->max_lod); + } + + if (tex->state.compare_func != state->compare_func || set_all) + glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_NEVER + state->compare_func); + + if (memcmp(&tex->state.border_color, &state->border_color, 16) || set_all) { + union pipe_color_union border_color; + if (get_swizzled_border_color(tview->format, &state->border_color, &border_color)) + glTexParameterIuiv(target, GL_TEXTURE_BORDER_COLOR, border_color.ui); + else + glTexParameterIuiv(target, GL_TEXTURE_BORDER_COLOR, state->border_color.ui); + } + tex->state = *state; +} + +static GLenum tgsitargettogltarget(const enum pipe_texture_target target, int nr_samples) +{ + switch(target) { + case PIPE_TEXTURE_1D: + return GL_TEXTURE_1D; + case PIPE_TEXTURE_2D: + return (nr_samples > 0) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + case PIPE_TEXTURE_3D: + return GL_TEXTURE_3D; + case PIPE_TEXTURE_RECT: + return GL_TEXTURE_RECTANGLE; + case PIPE_TEXTURE_CUBE: + return GL_TEXTURE_CUBE_MAP; + + case PIPE_TEXTURE_1D_ARRAY: + return GL_TEXTURE_1D_ARRAY; + case PIPE_TEXTURE_2D_ARRAY: + return (nr_samples > 0) ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY : GL_TEXTURE_2D_ARRAY; + case PIPE_TEXTURE_CUBE_ARRAY: + return GL_TEXTURE_CUBE_MAP_ARRAY; + case PIPE_BUFFER: + default: + return PIPE_BUFFER; + } + return PIPE_BUFFER; +} + +static ssize_t +write_full(int fd, const void *ptr, size_t count) +{ + const char *buf = ptr; + ssize_t ret = 0; + ssize_t total = 0; + + while (count) { + ret = write(fd, buf, count); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } + count -= ret; + buf += ret; + total += ret; + } + return total; +} + +int vrend_renderer_init(struct virgl_client *client, struct vrend_if_cbs *cbs) +{ + int gles_ver; + + client->vrend_state = CALLOC_STRUCT(vrend_state); + + vrend_object_init_resource_table(client); + + if (!vrend_clicbs) + vrend_clicbs = cbs; + + /* Give some defaults to be able to run the tests */ + client->vrend_state->max_texture_2d_size = + client->vrend_state->max_texture_3d_size = + client->vrend_state->max_texture_cube_size = 16384; + + gles_ver = vrend_gl_version(); + + if (!features_initialized) { + features_initialized = true; + init_features(gles_ver); + } + + glGetIntegerv(GL_MAX_DRAW_BUFFERS, (GLint *)&client->vrend_state->max_draw_buffers); + + vrend_resource_set_destroy_callback(vrend_destroy_resource_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_QUERY, vrend_destroy_query_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_SURFACE, vrend_destroy_surface_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_SHADER, vrend_destroy_shader_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_SAMPLER_VIEW, vrend_destroy_sampler_view_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_STREAMOUT_TARGET, vrend_destroy_so_target_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_SAMPLER_STATE, vrend_destroy_sampler_state_object); + vrend_object_set_destroy_callback(VIRGL_OBJECT_VERTEX_ELEMENTS, vrend_destroy_vertex_elements_object); + + if (!tex_conv_table_initialized) { + tex_conv_table_initialized = true; + vrend_build_format_list(); + vrend_check_texture_storage(tex_conv_table); + } + + list_inithead(&client->vrend_state->fence_list); + list_inithead(&client->vrend_state->fence_wait_list); + list_inithead(&client->vrend_state->waiting_query_list); + list_inithead(&client->vrend_state->active_ctx_list); + /* create 0 context */ + vrend_renderer_context_create_internal(client, 0); + + return 0; +} + +void +vrend_renderer_fini(struct virgl_client *client) +{ + if (!client->vrend_state) + return; + + typedef void (*destroy_callback)(void *); + vrend_resource_set_destroy_callback((destroy_callback)vrend_renderer_resource_destroy); + + vrend_blitter_fini(client); + vrend_decode_reset(client, false); + vrend_object_fini_resource_table(client); + vrend_decode_reset(client, true); + + client->vrend_state->current_ctx = NULL; + client->vrend_state->current_hw_ctx = NULL; +} + +static void vrend_destroy_sub_context(struct virgl_client *client, struct vrend_sub_context *sub) +{ + int i, j; + struct vrend_streamout_object *obj, *tmp; + + if (sub->fb_id) + glDeleteFramebuffers(1, &sub->fb_id); + + if (sub->blit_fb_ids[0]) + glDeleteFramebuffers(2, sub->blit_fb_ids); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + if (!has_feature(feat_gles31_vertex_attrib_binding)) { + while (sub->enabled_attribs_bitmask) { + i = u_bit_scan(&sub->enabled_attribs_bitmask); + + glDisableVertexAttribArray(i); + } + glDeleteVertexArrays(1, &sub->vaoid); + } + + glBindVertexArray(0); + + if (sub->current_so) + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); + + LIST_FOR_EACH_ENTRY_SAFE(obj, tmp, &sub->streamout_list, head) { + vrend_destroy_streamout_object(obj); + } + + vrend_shader_state_reference(&sub->shaders[PIPE_SHADER_VERTEX], NULL); + vrend_shader_state_reference(&sub->shaders[PIPE_SHADER_FRAGMENT], NULL); + vrend_shader_state_reference(&sub->shaders[PIPE_SHADER_GEOMETRY], NULL); + vrend_shader_state_reference(&sub->shaders[PIPE_SHADER_TESS_CTRL], NULL); + vrend_shader_state_reference(&sub->shaders[PIPE_SHADER_TESS_EVAL], NULL); + vrend_shader_state_reference(&sub->shaders[PIPE_SHADER_COMPUTE], NULL); + + if (sub->prog) + sub->prog->ref_context = NULL; + + vrend_free_programs(sub); + for (i = 0; i < PIPE_SHADER_TYPES; i++) { + free(sub->consts[i].consts); + sub->consts[i].consts = NULL; + + for (j = 0; j < PIPE_MAX_SHADER_SAMPLER_VIEWS; j++) { + vrend_sampler_view_reference(&sub->views[i].views[j], NULL); + } + } + + if (sub->zsurf) + vrend_surface_reference(&sub->zsurf, NULL); + + for (i = 0; i < sub->nr_cbufs; i++) { + if (!sub->surf[i]) + continue; + vrend_surface_reference(&sub->surf[i], NULL); + } + + vrend_resource_reference((struct vrend_resource **)&sub->ib.buffer, NULL); + + vrend_object_fini_ctx_table(sub->object_hash); + vrend_clicbs->destroy_gl_context(client, sub->gl_context); + + list_del(&sub->head); + FREE(sub); + +} + +bool vrend_destroy_context(struct vrend_context *ctx) +{ + bool switch_0 = (ctx == ctx->client->vrend_state->current_ctx); + struct vrend_context *cur = ctx->client->vrend_state->current_ctx; + struct vrend_sub_context *sub, *tmp; + if (switch_0) { + ctx->client->vrend_state->current_ctx = NULL; + ctx->client->vrend_state->current_hw_ctx = NULL; + } + + if (ctx->pstip_inited) + glDeleteTextures(1, &ctx->pstipple_tex_id); + ctx->pstip_inited = false; + + /* reset references on framebuffers */ + vrend_set_framebuffer_state(ctx, 0, NULL, 0); + + vrend_set_num_sampler_views(ctx, PIPE_SHADER_VERTEX, 0, 0); + vrend_set_num_sampler_views(ctx, PIPE_SHADER_FRAGMENT, 0, 0); + vrend_set_num_sampler_views(ctx, PIPE_SHADER_GEOMETRY, 0, 0); + vrend_set_num_sampler_views(ctx, PIPE_SHADER_TESS_CTRL, 0, 0); + vrend_set_num_sampler_views(ctx, PIPE_SHADER_TESS_EVAL, 0, 0); + vrend_set_num_sampler_views(ctx, PIPE_SHADER_COMPUTE, 0, 0); + + vrend_set_streamout_targets(ctx, 0, 0, NULL); + vrend_set_num_vbo(ctx, 0); + + vrend_set_index_buffer(ctx, 0, 0, 0); + + vrend_renderer_force_ctx_0(ctx->client); + LIST_FOR_EACH_ENTRY_SAFE(sub, tmp, &ctx->sub_ctxs, head) + vrend_destroy_sub_context(ctx->client, sub); + + vrend_object_fini_ctx_table(ctx->res_hash); + + list_del(&ctx->ctx_entry); + + FREE(ctx); + + if (!switch_0 && cur) + vrend_hw_switch_context(cur, true); + + return switch_0; +} + +struct vrend_context *vrend_create_context(struct virgl_client *client, int id) +{ + struct vrend_context *grctx = CALLOC_STRUCT(vrend_context); + + if (!grctx) + return NULL; + + grctx->ctx_id = id; + grctx->client = client; + + list_inithead(&grctx->sub_ctxs); + list_inithead(&grctx->active_nontimer_query_list); + + grctx->res_hash = vrend_object_init_ctx_table(); + + grctx->shader_cfg.use_explicit_locations = client->vrend_state->use_explicit_locations; + grctx->shader_cfg.max_draw_buffers = client->vrend_state->max_draw_buffers; + grctx->shader_cfg.has_es31_compat = has_feature(feat_gles31_compatibility); + + vrend_renderer_create_sub_ctx(grctx, 0); + vrend_renderer_set_sub_ctx(grctx, 0); + + vrend_get_glsl_version(&grctx->shader_cfg.glsl_version); + + list_addtail(&grctx->ctx_entry, &client->vrend_state->active_ctx_list); + + return grctx; +} + +int vrend_renderer_resource_attach_iov(struct virgl_client *client, int res_handle, struct iovec *iov, int num_iovs) +{ + struct vrend_resource *res; + + res = vrend_resource_lookup(client, res_handle, 0); + if (!res) + return EINVAL; + + if (res->iov) + return 0; + + /* work out size and max resource size */ + res->iov = iov; + res->num_iovs = num_iovs; + + if (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY)) { + vrend_write_to_iovec(res->iov, res->num_iovs, 0, + res->ptr, res->base.width0); + } + + return 0; +} + +void vrend_renderer_resource_detach_iov(struct virgl_client *client, int res_handle, struct iovec **iov_p, int *num_iovs_p) +{ + struct vrend_resource *res; + res = vrend_resource_lookup(client, res_handle, 0); + if (!res) { + return; + } + if (iov_p) + *iov_p = res->iov; + if (num_iovs_p) + *num_iovs_p = res->num_iovs; + + if (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY)) { + vrend_read_from_iovec(res->iov, res->num_iovs, 0, + res->ptr, res->base.width0); + } + + res->iov = NULL; + res->num_iovs = 0; +} + +static int check_resource_valid(struct virgl_client *client, struct vrend_renderer_resource_create_args *args) +{ + /* do not accept handle 0 */ + if (args->handle == 0) + return -1; + + /* limit the target */ + if (args->target >= PIPE_MAX_TEXTURE_TYPES) + return -1; + + if (args->format >= VIRGL_FORMAT_MAX) + return -1; + + bool format_can_texture_storage = has_feature(feat_texture_storage) && + (tex_conv_table[args->format].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE); + + /* only texture 2d and 2d array can have multiple samples */ + if (args->nr_samples > 0) { + if (!has_feature(feat_texture_multisample)) + return -1; + + if (args->target != PIPE_TEXTURE_2D && args->target != PIPE_TEXTURE_2D_ARRAY) + return -1; + + /* multisample can't have miplevels */ + if (args->last_level > 0) + return -1; + } + + if (args->last_level > 0) { + /* buffer and rect textures can't have mipmaps */ + if (args->target == PIPE_BUFFER) + return -1; + + if (args->target == PIPE_TEXTURE_RECT) + return -1; + + if (args->last_level > (floor(log2(MAX2(args->width, args->height))) + 1)) + return -1; + } + + if (args->flags != 0 && args->flags != VIRGL_RESOURCE_Y_0_TOP) + return -1; + + if (args->flags & VIRGL_RESOURCE_Y_0_TOP) { + if (args->target != PIPE_TEXTURE_2D && args->target != PIPE_TEXTURE_RECT) + return -1; + } + + /* array size for array textures only */ + if (args->target == PIPE_TEXTURE_CUBE) { + if (args->array_size != 6) + return -1; + } else if (args->target == PIPE_TEXTURE_CUBE_ARRAY) { + if (!has_feature(feat_cube_map_array)) + return -1; + + if (args->array_size % 6) + return -1; + } else if (args->array_size > 1) { + if (args->target != PIPE_TEXTURE_2D_ARRAY && + args->target != PIPE_TEXTURE_1D_ARRAY) + return -1; + + if (!has_feature(feat_texture_array)) + return -1; + } + + if (format_can_texture_storage && !args->width) + return -1; + + if (args->bind == 0 || + args->bind == VIRGL_BIND_CUSTOM || + args->bind == VIRGL_BIND_STAGING || + args->bind == VIRGL_BIND_INDEX_BUFFER || + args->bind == VIRGL_BIND_STREAM_OUTPUT || + args->bind == VIRGL_BIND_VERTEX_BUFFER || + args->bind == VIRGL_BIND_CONSTANT_BUFFER || + args->bind == VIRGL_BIND_QUERY_BUFFER || + args->bind == VIRGL_BIND_COMMAND_ARGS || + args->bind == VIRGL_BIND_SHADER_BUFFER) { + if (args->target != PIPE_BUFFER) + return -1; + + if (args->height != 1 || args->depth != 1) + return -1; + + if (args->bind == VIRGL_BIND_QUERY_BUFFER) + return -1; + + if (args->bind == VIRGL_BIND_COMMAND_ARGS && !has_feature(feat_indirect_draw)) + return -1; + } else { + if (!((args->bind & VIRGL_BIND_SAMPLER_VIEW) || + (args->bind & VIRGL_BIND_DEPTH_STENCIL) || + (args->bind & VIRGL_BIND_RENDER_TARGET) || + (args->bind & VIRGL_BIND_CURSOR) || + (args->bind & VIRGL_BIND_SHARED) || + (args->bind & VIRGL_BIND_LINEAR))) + return -1; + + if (args->target == PIPE_TEXTURE_2D || + args->target == PIPE_TEXTURE_RECT || + args->target == PIPE_TEXTURE_CUBE || + args->target == PIPE_TEXTURE_2D_ARRAY || + args->target == PIPE_TEXTURE_CUBE_ARRAY) { + if (args->depth != 1) + return -1; + + if (format_can_texture_storage && !args->height) + return -1; + } + if (args->target == PIPE_TEXTURE_1D || + args->target == PIPE_TEXTURE_1D_ARRAY) { + if (args->height != 1 || args->depth != 1) + return -1; + + if (args->width > client->vrend_state->max_texture_2d_size) + return -1; + } + + if (args->target == PIPE_TEXTURE_2D || + args->target == PIPE_TEXTURE_RECT || + args->target == PIPE_TEXTURE_2D_ARRAY) { + if (args->width > client->vrend_state->max_texture_2d_size || + args->height > client->vrend_state->max_texture_2d_size) + return -1; + } + + if (args->target == PIPE_TEXTURE_3D) { + if (format_can_texture_storage && + (!args->height || !args->depth)) + return -1; + + if (args->width > client->vrend_state->max_texture_3d_size || + args->height > client->vrend_state->max_texture_3d_size || + args->depth > client->vrend_state->max_texture_3d_size) + return -1; + } + if (args->target == PIPE_TEXTURE_2D_ARRAY || + args->target == PIPE_TEXTURE_CUBE_ARRAY || + args->target == PIPE_TEXTURE_1D_ARRAY) { + if (format_can_texture_storage && + !args->array_size) + return -1; + } + if (args->target == PIPE_TEXTURE_CUBE || + args->target == PIPE_TEXTURE_CUBE_ARRAY) { + if (args->width != args->height) + return -1; + + if (args->width > client->vrend_state->max_texture_cube_size) + return -1; + } + } + return 0; +} + +static void vrend_create_buffer(struct vrend_resource *gr, uint32_t width) +{ + gr->storage_bits |= VREND_STORAGE_GL_BUFFER; + + glGenBuffers(1, &gr->id); + glBindBuffer(gr->target, gr->id); + glBufferData(gr->target, width, NULL, GL_STREAM_DRAW); + glBindBuffer(gr->target, 0); +} + +static inline void +vrend_renderer_resource_copy_args(struct vrend_renderer_resource_create_args *args, + struct vrend_resource *gr) +{ + assert(gr); + assert(args); + + gr->handle = args->handle; + gr->base.bind = args->bind; + gr->base.width0 = args->width; + gr->base.height0 = args->height; + gr->base.depth0 = args->depth; + gr->base.format = args->format; + gr->base.target = args->target; + gr->base.last_level = args->last_level; + gr->base.nr_samples = args->nr_samples; + gr->base.array_size = args->array_size; +} + +static int vrend_renderer_resource_allocate_texture(struct vrend_resource *gr) +{ + uint level; + GLenum internalformat, glformat, gltype; + enum virgl_formats format = gr->base.format; + struct vrend_texture *gt = (struct vrend_texture *)gr; + struct pipe_resource *pr = &gr->base; + + if (pr->width0 == 0) + return EINVAL; + + bool format_can_texture_storage = has_feature(feat_texture_storage) && + (tex_conv_table[format].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE); + + /* On GLES there is no support for glTexImage*DMultisample and + * BGRA surfaces are also unlikely to support glTexStorage2DMultisample + * so we try to emulate here + */ + if (pr->nr_samples > 0 && !format_can_texture_storage) + gr->base.bind |= VIRGL_BIND_PREFER_EMULATED_BGRA; + + format_can_texture_storage = has_feature(feat_texture_storage) && + (tex_conv_table[format].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE); + + if (format_can_texture_storage) + gr->storage_bits |= VREND_STORAGE_GL_IMMUTABLE; + + gr->target = tgsitargettogltarget(pr->target, pr->nr_samples); + gr->target = translate_gles_emulation_texture_target(gr->target); + gr->storage_bits |= VREND_STORAGE_GL_TEXTURE; + + glGenTextures(1, &gr->id); + glBindTexture(gr->target, gr->id); + + internalformat = tex_conv_table[format].internalformat; + glformat = tex_conv_table[format].glformat; + gltype = tex_conv_table[format].gltype; + + if (internalformat == 0) { + glBindTexture(gr->target, 0); + FREE(gt); + return EINVAL; + } + + if (pr->nr_samples > 0) { + if (format_can_texture_storage) { + if (gr->target == GL_TEXTURE_2D_MULTISAMPLE) { + glTexStorage2DMultisample(gr->target, pr->nr_samples, + internalformat, pr->width0, pr->height0, + GL_TRUE); + } else { + glTexStorage3DMultisample(gr->target, pr->nr_samples, + internalformat, pr->width0, pr->height0, pr->array_size, + GL_TRUE); + } + } + } else if (gr->target == GL_TEXTURE_CUBE_MAP) { + int i; + if (format_can_texture_storage) + glTexStorage2D(GL_TEXTURE_CUBE_MAP, pr->last_level + 1, internalformat, pr->width0, pr->height0); + else { + for (i = 0; i < 6; i++) { + GLenum ctarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; + for (level = 0; level <= pr->last_level; level++) { + unsigned mwidth = u_minify(pr->width0, level); + unsigned mheight = u_minify(pr->height0, level); + + glTexImage2D(ctarget, level, internalformat, mwidth, mheight, 0, glformat, + gltype, NULL); + } + } + } + } else if (gr->target == GL_TEXTURE_3D || + gr->target == GL_TEXTURE_2D_ARRAY || + gr->target == GL_TEXTURE_CUBE_MAP_ARRAY) { + if (format_can_texture_storage) { + unsigned depth_param = (gr->target == GL_TEXTURE_2D_ARRAY || gr->target == GL_TEXTURE_CUBE_MAP_ARRAY) ? + pr->array_size : pr->depth0; + glTexStorage3D(gr->target, pr->last_level + 1, internalformat, pr->width0, pr->height0, depth_param); + } else { + for (level = 0; level <= pr->last_level; level++) { + unsigned depth_param = (gr->target == GL_TEXTURE_2D_ARRAY || gr->target == GL_TEXTURE_CUBE_MAP_ARRAY) ? + pr->array_size : u_minify(pr->depth0, level); + unsigned mwidth = u_minify(pr->width0, level); + unsigned mheight = u_minify(pr->height0, level); + glTexImage3D(gr->target, level, internalformat, mwidth, mheight, + depth_param, 0, glformat, gltype, NULL); + } + } + } else { + if (format_can_texture_storage) + glTexStorage2D(gr->target, pr->last_level + 1, internalformat, pr->width0, + gr->target == GL_TEXTURE_1D_ARRAY ? pr->array_size : pr->height0); + else { + for (level = 0; level <= pr->last_level; level++) { + unsigned mwidth = u_minify(pr->width0, level); + unsigned mheight = u_minify(pr->height0, level); + glTexImage2D(gr->target, level, internalformat, mwidth, + gr->target == GL_TEXTURE_1D_ARRAY ? pr->array_size : mheight, + 0, glformat, gltype, NULL); + } + } + } + + if (!format_can_texture_storage) { + glTexParameteri(gr->target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(gr->target, GL_TEXTURE_MAX_LEVEL, pr->last_level); + } + + glBindTexture(gr->target, 0); + + gt->state.max_lod = -1; + gt->cur_swizzle_r = gt->cur_swizzle_g = gt->cur_swizzle_b = gt->cur_swizzle_a = -1; + gt->cur_base = -1; + gt->cur_max = 10000; + return 0; +} + +int vrend_renderer_resource_create(struct virgl_client *client, struct vrend_renderer_resource_create_args *args, struct iovec *iov, uint32_t num_iovs) +{ + struct vrend_resource *gr; + int ret; + + ret = check_resource_valid(client, args); + if (ret) + return EINVAL; + + gr = (struct vrend_resource *)CALLOC_STRUCT(vrend_texture); + if (!gr) + return ENOMEM; + + vrend_renderer_resource_copy_args(args, gr); + gr->iov = iov; + gr->num_iovs = num_iovs; + gr->storage_bits = VREND_STORAGE_GUEST_MEMORY; + + if (args->flags & VIRGL_RESOURCE_Y_0_TOP) + gr->y_0_top = true; + + pipe_reference_init(&gr->base.reference, 1); + + if (args->target == PIPE_BUFFER) { + if (args->bind == VIRGL_BIND_CUSTOM) { + /* use iovec directly when attached */ + gr->storage_bits |= VREND_STORAGE_HOST_SYSTEM_MEMORY; + gr->ptr = malloc(args->width); + if (!gr->ptr) { + FREE(gr); + return ENOMEM; + } + } else if (args->bind == VIRGL_BIND_STAGING) { + /* staging buffers only use guest memory -- nothing to do. */ + } else if (args->bind == VIRGL_BIND_INDEX_BUFFER) { + gr->target = GL_ELEMENT_ARRAY_BUFFER; + vrend_create_buffer(gr, args->width); + } else if (args->bind == VIRGL_BIND_STREAM_OUTPUT) { + gr->target = GL_TRANSFORM_FEEDBACK_BUFFER; + vrend_create_buffer(gr, args->width); + } else if (args->bind == VIRGL_BIND_VERTEX_BUFFER) { + gr->target = GL_ARRAY_BUFFER; + vrend_create_buffer(gr, args->width); + } else if (args->bind == VIRGL_BIND_CONSTANT_BUFFER) { + gr->target = GL_UNIFORM_BUFFER; + vrend_create_buffer(gr, args->width); + } else if (args->bind == VIRGL_BIND_COMMAND_ARGS) { + gr->target = GL_DRAW_INDIRECT_BUFFER; + vrend_create_buffer(gr, args->width); + } else if (args->bind == 0 || args->bind == VIRGL_BIND_SHADER_BUFFER) { + gr->target = GL_ARRAY_BUFFER; + vrend_create_buffer(gr, args->width); + } else if (args->bind & VIRGL_BIND_SAMPLER_VIEW) { + /* + * On Desktop we use GL_ARB_texture_buffer_object on GLES we use + * GL_EXT_texture_buffer (it is in the ANDRIOD extension pack). + */ +#if GL_TEXTURE_BUFFER != GL_TEXTURE_BUFFER_EXT +#error "GL_TEXTURE_BUFFER enums differ, they shouldn't." +#endif + + /* need to check GL version here */ + if (has_feature(feat_arb_or_gles_ext_texture_buffer)) { + gr->target = GL_TEXTURE_BUFFER; + } else { + gr->target = GL_PIXEL_PACK_BUFFER; + } + vrend_create_buffer(gr, args->width); + } else { + FREE(gr); + return EINVAL; + } + } else { + int r = vrend_renderer_resource_allocate_texture(gr); + if (r) { + FREE(gr); + return r; + } + } + + ret = vrend_resource_insert(client, gr, args->handle); + if (ret == 0) { + vrend_renderer_resource_destroy(gr); + return ENOMEM; + } + return 0; +} + +void vrend_renderer_resource_destroy(struct vrend_resource *res) +{ + if (res->readback_fb_id) + glDeleteFramebuffers(1, &res->readback_fb_id); + + if (has_bit(res->storage_bits, VREND_STORAGE_GL_TEXTURE)) { + glDeleteTextures(1, &res->id); + } else if (has_bit(res->storage_bits, VREND_STORAGE_GL_BUFFER)) { + glDeleteBuffers(1, &res->id); + if (res->tbo_tex_id) + glDeleteTextures(1, &res->tbo_tex_id); + } else if (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY)) { + free(res->ptr); + } + + free(res); +} + +static void vrend_destroy_resource_object(void *obj_ptr) +{ + struct vrend_resource *res = obj_ptr; + + if (pipe_reference(&res->base.reference, NULL)) + vrend_renderer_resource_destroy(res); +} + +void vrend_renderer_resource_unref(struct virgl_client *client, uint32_t res_handle) +{ + struct vrend_resource *res; + struct vrend_context *ctx; + + res = vrend_resource_lookup(client, res_handle, 0); + if (!res) + return; + + /* find in all contexts and detach also */ + + /* remove from any contexts */ + LIST_FOR_EACH_ENTRY(ctx, &client->vrend_state->active_ctx_list, ctx_entry) { + vrend_renderer_detach_res_ctx(ctx, res->handle); + } + + vrend_resource_remove(client, res->handle); +} + +struct virgl_sub_upload_data { + GLenum target; + struct pipe_box *box; +}; + +static void iov_buffer_upload(void *cookie, uint32_t doff, void *src, int len) +{ + struct virgl_sub_upload_data *d = cookie; + glBufferSubData(d->target, d->box->x + doff, len, src); +} + +static void vrend_scale_depth(void *ptr, int size, float scale_val) +{ + GLuint *ival = ptr; + const GLfloat myscale = 1.0f / 0xffffff; + int i; + for (i = 0; i < size / 4; i++) { + GLuint value = ival[i]; + GLfloat d = ((float)(value >> 8) * myscale) * scale_val; + d = CLAMP(d, 0.0F, 1.0F); + ival[i] = (int)(d / myscale) << 8; + } +} + +static void read_transfer_data(struct iovec *iov, + unsigned int num_iovs, + char *data, + enum virgl_formats format, + uint64_t offset, + uint32_t src_stride, + uint32_t src_layer_stride, + struct pipe_box *box, + bool invert) +{ + int blsize = util_format_get_blocksize(format); + uint32_t size = vrend_get_iovec_size(iov, num_iovs); + uint32_t send_size = util_format_get_nblocks(format, box->width, + box->height) * blsize * box->depth; + uint32_t bwx = util_format_get_nblocksx(format, box->width) * blsize; + int32_t bh = util_format_get_nblocksy(format, box->height); + int d, h; + + if ((send_size == size || bh == 1) && !invert && box->depth == 1) + vrend_read_from_iovec(iov, num_iovs, offset, data, send_size); + else { + if (invert) { + for (d = 0; d < box->depth; d++) { + uint32_t myoffset = offset + d * src_layer_stride; + for (h = bh - 1; h >= 0; h--) { + void *ptr = data + (h * bwx) + d * (bh * bwx); + vrend_read_from_iovec(iov, num_iovs, myoffset, ptr, bwx); + myoffset += src_stride; + } + } + } else { + for (d = 0; d < box->depth; d++) { + uint32_t myoffset = offset + d * src_layer_stride; + for (h = 0; h < bh; h++) { + void *ptr = data + (h * bwx) + d * (bh * bwx); + vrend_read_from_iovec(iov, num_iovs, myoffset, ptr, bwx); + myoffset += src_stride; + } + } + } + } +} + +static void write_transfer_data(struct pipe_resource *res, + struct iovec *iov, + unsigned num_iovs, + char *data, + uint32_t dst_stride, + struct pipe_box *box, + uint32_t level, + uint64_t offset, + bool invert) +{ + int blsize = util_format_get_blocksize(res->format); + uint32_t size = vrend_get_iovec_size(iov, num_iovs); + uint32_t send_size = util_format_get_nblocks(res->format, box->width, + box->height) * blsize * box->depth; + uint32_t bwx = util_format_get_nblocksx(res->format, box->width) * blsize; + int32_t bh = util_format_get_nblocksy(res->format, box->height); + int d, h; + uint32_t stride = dst_stride ? dst_stride : util_format_get_nblocksx(res->format, u_minify(res->width0, level)) * blsize; + + if ((send_size == size || bh == 1) && !invert && box->depth == 1) { + vrend_write_to_iovec(iov, num_iovs, offset, data, send_size); + } else if (invert) { + for (d = 0; d < box->depth; d++) { + uint32_t myoffset = offset + d * stride * u_minify(res->height0, level); + for (h = bh - 1; h >= 0; h--) { + void *ptr = data + (h * bwx) + d * (bh * bwx); + vrend_write_to_iovec(iov, num_iovs, myoffset, ptr, bwx); + myoffset += stride; + } + } + } else { + for (d = 0; d < box->depth; d++) { + uint32_t myoffset = offset + d * stride * u_minify(res->height0, level); + for (h = 0; h < bh; h++) { + void *ptr = data + (h * bwx) + d * (bh * bwx); + vrend_write_to_iovec(iov, num_iovs, myoffset, ptr, bwx); + myoffset += stride; + } + } + } +} + +static bool check_transfer_bounds(struct vrend_resource *res, + const struct vrend_transfer_info *info) +{ + int lwidth, lheight; + + /* check mipmap level is in bounds */ + if (info->level > res->base.last_level) + return false; + if (info->box->x < 0 || info->box->y < 0) + return false; + /* these will catch bad y/z/w/d with 1D textures etc */ + lwidth = u_minify(res->base.width0, info->level); + if (info->box->width > lwidth || info->box->width < 0) + return false; + if (info->box->x > lwidth) + return false; + if (info->box->width + info->box->x > lwidth) + return false; + + lheight = u_minify(res->base.height0, info->level); + if (info->box->height > lheight || info->box->height < 0) + return false; + if (info->box->y > lheight) + return false; + if (info->box->height + info->box->y > lheight) + return false; + + if (res->base.target == PIPE_TEXTURE_3D) { + int ldepth = u_minify(res->base.depth0, info->level); + if (info->box->depth > ldepth || info->box->depth < 0) + return false; + if (info->box->z > ldepth) + return false; + if (info->box->z + info->box->depth > ldepth) + return false; + } else { + if (info->box->depth > (int)res->base.array_size) + return false; + if (info->box->z > (int)res->base.array_size) + return false; + if (info->box->z + info->box->depth > (int)res->base.array_size) + return false; + } + + return true; +} + +/* Calculate the size of the memory needed to hold all the data of a + * transfer for particular stride values. + */ +static uint64_t vrend_transfer_size(struct vrend_resource *vres, + const struct vrend_transfer_info *info, + uint32_t stride, uint32_t layer_stride) +{ + struct pipe_resource *pres = &vres->base; + struct pipe_box *box = info->box; + uint64_t size; + /* For purposes of size calculation, assume that invalid dimension values + * correspond to 1. + */ + int w = box->width > 0 ? box->width : 1; + int h = box->height > 0 ? box->height : 1; + int d = box->depth > 0 ? box->depth : 1; + int nblocksx = util_format_get_nblocksx(pres->format, w); + int nblocksy = util_format_get_nblocksy(pres->format, h); + + /* Calculate the box size, not including the last layer. The last layer + * is the only one which may be incomplete, and is the only layer for + * non 3d/2d-array formats. + */ + size = (d - 1) * layer_stride; + /* Calculate the size of the last (or only) layer, not including the last + * block row. The last block row is the only one which may be incomplete and + * is the only block row for non 2d/1d-array formats. + */ + size += (nblocksy - 1) * stride; + /* Calculate the size of the the last (or only) block row. */ + size += nblocksx * util_format_get_blocksize(pres->format); + + return size; +} + +static bool check_iov_bounds(struct vrend_resource *res, + const struct vrend_transfer_info *info, + struct iovec *iov, int num_iovs) +{ + GLuint transfer_size; + GLuint iovsize = vrend_get_iovec_size(iov, num_iovs); + GLuint valid_stride, valid_layer_stride; + + /* If the transfer specifies a stride, verify that it's at least as large as + * the minimum required for the transfer. If no stride is specified use the + * image stride for the specified level. + */ + if (info->stride) { + GLuint min_stride = util_format_get_stride(res->base.format, info->box->width); + if (info->stride < min_stride) + return false; + valid_stride = info->stride; + } else { + valid_stride = util_format_get_stride(res->base.format, + u_minify(res->base.width0, info->level)); + } + + /* If the transfer specifies a layer_stride, verify that it's at least as + * large as the minimum required for the transfer. If no layer_stride is + * specified use the image layer_stride for the specified level. + */ + if (info->layer_stride) { + GLuint min_layer_stride = util_format_get_2d_size(res->base.format, + valid_stride, + info->box->height); + if (info->layer_stride < min_layer_stride) + return false; + valid_layer_stride = info->layer_stride; + } else { + valid_layer_stride = + util_format_get_2d_size(res->base.format, valid_stride, + u_minify(res->base.height0, info->level)); + } + + /* Calculate the size required for the transferred data, based on the + * calculated or provided strides, and ensure that the iov, starting at the + * specified offset, is able to hold at least that size. + */ + transfer_size = vrend_transfer_size(res, info, + valid_stride, + valid_layer_stride); + if (iovsize < info->offset) + return false; + if (iovsize < transfer_size) + return false; + if (iovsize < info->offset + transfer_size) + return false; + + return true; +} + +static int vrend_renderer_transfer_write_iov(struct vrend_context *ctx, + struct vrend_resource *res, + struct iovec *iov, int num_iovs, + const struct vrend_transfer_info *info) +{ + void *data; + + if (is_only_bit(res->storage_bits, VREND_STORAGE_GUEST_MEMORY) || + (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY) && res->iov)) { + return vrend_copy_iovec(iov, num_iovs, info->offset, + res->iov, res->num_iovs, info->box->x, + info->box->width, res->ptr); + } + + if (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY)) { + assert(!res->iov); + vrend_read_from_iovec(iov, num_iovs, info->offset, + res->ptr + info->box->x, info->box->width); + return 0; + } + + if (has_bit(res->storage_bits, VREND_STORAGE_GL_BUFFER)) { + GLuint map_flags = GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_WRITE_BIT; + struct virgl_sub_upload_data d; + d.box = info->box; + d.target = res->target; + + if (!info->synchronized) + map_flags |= GL_MAP_UNSYNCHRONIZED_BIT; + + glBindBuffer(res->target, res->id); + data = glMapBufferRange(res->target, info->box->x, info->box->width, map_flags); + if (data == NULL) { + vrend_read_from_iovec_cb(iov, num_iovs, info->offset, info->box->width, &iov_buffer_upload, &d); + } else { + vrend_read_from_iovec(iov, num_iovs, info->offset, data, info->box->width); + glUnmapBuffer(res->target); + } + glBindBuffer(res->target, 0); + } else { + GLenum glformat; + GLenum gltype; + int need_temp = 0; + int elsize = util_format_get_blocksize(res->base.format); + int x = 0, y = 0; + bool compressed; + bool invert = false; + float depth_scale; + GLuint send_size = 0; + uint32_t stride = info->stride; + uint32_t layer_stride = info->layer_stride; + + if (ctx) + vrend_use_program(ctx, 0); + else + glUseProgram(0); + + if (!stride) + stride = util_format_get_nblocksx(res->base.format, u_minify(res->base.width0, info->level)) * elsize; + + if (!layer_stride) + layer_stride = util_format_get_2d_size(res->base.format, stride, + u_minify(res->base.height0, info->level)); + + compressed = util_format_is_compressed(res->base.format); + if (num_iovs > 1 || compressed) { + need_temp = true; + } + + if ((res->y_0_top || (res->base.format == VIRGL_FORMAT_Z24X8_UNORM))) { + need_temp = true; + if (res->y_0_top) + invert = true; + } + + send_size = util_format_get_nblocks(res->base.format, info->box->width, + info->box->height) * elsize; + if (res->target == GL_TEXTURE_3D || + res->target == GL_TEXTURE_2D_ARRAY || + res->target == GL_TEXTURE_CUBE_MAP_ARRAY) + send_size *= info->box->depth; + + if (need_temp) { + data = malloc(send_size); + if (!data) + return ENOMEM; + read_transfer_data(iov, num_iovs, data, res->base.format, info->offset, + stride, layer_stride, info->box, invert); + } else { + if (send_size > iov[0].iov_len - info->offset) + return EINVAL; + data = (char*)iov[0].iov_base + info->offset; + } + + if (!need_temp) { + assert(stride); + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / elsize); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, layer_stride / stride); + } else + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + switch (elsize) { + case 1: + case 3: + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + break; + case 2: + case 6: + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + break; + case 4: + default: + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + break; + case 8: + glPixelStorei(GL_UNPACK_ALIGNMENT, 8); + break; + } + + glformat = tex_conv_table[res->base.format].glformat; + gltype = tex_conv_table[res->base.format].gltype; + + uint32_t comp_size; + glBindTexture(res->target, res->id); + + if (compressed) { + glformat = tex_conv_table[res->base.format].internalformat; + comp_size = util_format_get_nblocks(res->base.format, info->box->width, + info->box->height) * util_format_get_blocksize(res->base.format); + } + + if (glformat == 0) { + glformat = GL_BGRA_EXT; + gltype = GL_UNSIGNED_BYTE; + } + + x = info->box->x; + y = invert ? (int)res->base.height0 - info->box->y - info->box->height : info->box->y; + + /* mipmaps are usually passed in one iov, and we need to keep the offset + * into the data in case we want to read back the data of a surface + * that can not be rendered. Since we can not assume that the whole texture + * is filled, we evaluate the offset for origin (0,0,0). Since it is also + * possible that a resource is reused and resized update the offset every time. + */ + if (info->level < VR_MAX_TEXTURE_2D_LEVELS) { + int64_t level_height = u_minify(res->base.height0, info->level); + res->mipmap_offsets[info->level] = info->offset - + ((info->box->z * level_height + y) * stride + x * elsize); + } + + if (res->base.format == VIRGL_FORMAT_Z24X8_UNORM) { + /* we get values from the guest as 24-bit scaled integers + but we give them to the host GL and it interprets them + as 32-bit scaled integers, so we need to scale them here */ + depth_scale = 256.0; + vrend_scale_depth(data, send_size, depth_scale); + } + if (res->target == GL_TEXTURE_CUBE_MAP) { + GLenum ctarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + info->box->z; + if (compressed) { + glCompressedTexSubImage2D(ctarget, info->level, x, y, + info->box->width, info->box->height, + glformat, comp_size, data); + } else { + glTexSubImage2D(ctarget, info->level, x, y, info->box->width, info->box->height, + glformat, gltype, data); + } + } else if (res->target == GL_TEXTURE_3D || res->target == GL_TEXTURE_2D_ARRAY || res->target == GL_TEXTURE_CUBE_MAP_ARRAY) { + if (compressed) { + glCompressedTexSubImage3D(res->target, info->level, x, y, info->box->z, + info->box->width, info->box->height, info->box->depth, + glformat, comp_size, data); + } else { + glTexSubImage3D(res->target, info->level, x, y, info->box->z, + info->box->width, info->box->height, info->box->depth, + glformat, gltype, data); + } + } else { + if (compressed) { + glCompressedTexSubImage2D(res->target, info->level, x, res->target == GL_TEXTURE_1D_ARRAY ? info->box->z : y, + info->box->width, info->box->height, + glformat, comp_size, data); + } else { + glTexSubImage2D(res->target, info->level, x, res->target == GL_TEXTURE_1D_ARRAY ? info->box->z : y, + info->box->width, + res->target == GL_TEXTURE_1D_ARRAY ? info->box->depth : info->box->height, + glformat, gltype, data); + } + } + + glBindTexture(res->target, 0); + + if (stride && !need_temp) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + if (need_temp) + free(data); + } + return 0; +} + +static uint32_t vrend_get_texture_depth(struct vrend_resource *res, uint32_t level) +{ + uint32_t depth = 1; + if (res->target == GL_TEXTURE_3D) + depth = u_minify(res->base.depth0, level); + else if (res->target == GL_TEXTURE_1D_ARRAY || res->target == GL_TEXTURE_2D_ARRAY || + res->target == GL_TEXTURE_CUBE_MAP || res->target == GL_TEXTURE_CUBE_MAP_ARRAY) + depth = res->base.array_size; + + return depth; +} + +static void do_readpixels(GLint x, GLint y, + GLsizei width, GLsizei height, + GLenum format, GLenum type, + GLsizei bufSize, void *data) +{ + glReadPixels(x, y, width, height, format, type, data); +} + +static int vrend_transfer_send_readpixels(struct vrend_resource *res, + struct iovec *iov, int num_iovs, + const struct vrend_transfer_info *info) +{ + char *myptr = (char*)iov[0].iov_base + info->offset; + int need_temp = 0; + GLuint fb_id; + char *data; + bool actually_invert, separate_invert = false; + GLenum format, type; + GLint y1; + uint32_t send_size = 0; + uint32_t h = u_minify(res->base.height0, info->level); + int elsize = util_format_get_blocksize(res->base.format); + float depth_scale; + int row_stride = info->stride / elsize; + GLint old_fbo; + + glUseProgram(0); + + enum virgl_formats fmt = res->base.format; + format = tex_conv_table[fmt].glformat; + type = tex_conv_table[fmt].gltype; + /* if we are asked to invert and reading from a front then don't */ + + actually_invert = res->y_0_top; + + if (actually_invert) + separate_invert = true; + + if (num_iovs > 1 || separate_invert) + need_temp = 1; + + if (need_temp) { + send_size = util_format_get_nblocks(res->base.format, info->box->width, info->box->height) * info->box->depth * util_format_get_blocksize(res->base.format); + data = malloc(send_size); + if (!data) + return ENOMEM; + } else { + send_size = iov[0].iov_len - info->offset; + data = myptr; + if (!row_stride) + row_stride = util_format_get_nblocksx(res->base.format, u_minify(res->base.width0, info->level)); + } + + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_fbo); + + if (res->readback_fb_id == 0 || (int)res->readback_fb_level != info->level || + (int)res->readback_fb_z != info->box->z) { + + if (res->readback_fb_id) + glDeleteFramebuffers(1, &res->readback_fb_id); + + glGenFramebuffers(1, &fb_id); + glBindFramebuffer(GL_FRAMEBUFFER, fb_id); + + vrend_fb_bind_texture(res, 0, info->level, info->box->z); + + res->readback_fb_id = fb_id; + res->readback_fb_level = info->level; + res->readback_fb_z = info->box->z; + } else + glBindFramebuffer(GL_FRAMEBUFFER, res->readback_fb_id); + if (actually_invert) + y1 = h - info->box->y - info->box->height; + else + y1 = info->box->y; + + if (!vrend_format_is_ds(res->base.format)) + glReadBuffer(GL_COLOR_ATTACHMENT0); + if (!need_temp && row_stride) + glPixelStorei(GL_PACK_ROW_LENGTH, row_stride); + + switch (elsize) { + case 1: + glPixelStorei(GL_PACK_ALIGNMENT, 1); + break; + case 2: + glPixelStorei(GL_PACK_ALIGNMENT, 2); + break; + case 4: + default: + glPixelStorei(GL_PACK_ALIGNMENT, 4); + break; + case 8: + glPixelStorei(GL_PACK_ALIGNMENT, 8); + break; + } + + if (res->base.format == VIRGL_FORMAT_Z24X8_UNORM) { + /* we get values from the guest as 24-bit scaled integers + but we give them to the host GL and it interprets them + as 32-bit scaled integers, so we need to scale them here */ + depth_scale = 1.0 / 256.0; + } + + do_readpixels(info->box->x, y1, info->box->width, info->box->height, format, type, send_size, data); + + if (res->base.format == VIRGL_FORMAT_Z24X8_UNORM) { + vrend_scale_depth(data, send_size, depth_scale); + } + + if (!need_temp && row_stride) + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + if (need_temp) { + write_transfer_data(&res->base, iov, num_iovs, data, + info->stride, info->box, info->level, info->offset, + separate_invert); + free(data); + } + + glBindFramebuffer(GL_FRAMEBUFFER, old_fbo); + + return 0; +} + +static int vrend_transfer_send_readonly(struct vrend_resource *res, + struct iovec *iov, int num_iovs, + UNUSED const struct vrend_transfer_info *info) +{ + bool same_iov = true; + uint i; + + if (res->num_iovs == (uint32_t)num_iovs) { + for (i = 0; i < res->num_iovs; i++) { + if (res->iov[i].iov_len != iov[i].iov_len || + res->iov[i].iov_base != iov[i].iov_base) { + same_iov = false; + } + } + } else { + same_iov = false; + } + + /* + * When we detect that we are reading back to the same iovs that are + * attached to the resource and we know that the resource can not + * be rendered to (as this function is only called then), we do not + * need to do anything more. + */ + if (same_iov) { + return 0; + } + + return -1; +} + +static int vrend_renderer_transfer_send_iov(struct vrend_resource *res, + struct iovec *iov, int num_iovs, + const struct vrend_transfer_info *info) +{ + if (is_only_bit(res->storage_bits, VREND_STORAGE_GUEST_MEMORY) || + (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY) && res->iov)) { + return vrend_copy_iovec(res->iov, res->num_iovs, info->box->x, + iov, num_iovs, info->offset, + info->box->width, res->ptr); + } + + if (has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY)) { + assert(!res->iov); + vrend_write_to_iovec(iov, num_iovs, info->offset, + res->ptr + info->box->x, info->box->width); + return 0; + } + + if (has_bit(res->storage_bits, VREND_STORAGE_GL_BUFFER)) { + uint32_t send_size = info->box->width * util_format_get_blocksize(res->base.format); + void *data; + + glBindBuffer(res->target, res->id); + data = glMapBufferRange(res->target, info->box->x, info->box->width, GL_MAP_READ_BIT); + if (data) + vrend_write_to_iovec(iov, num_iovs, info->offset, data, send_size); + glUnmapBuffer(res->target); + glBindBuffer(res->target, 0); + } else { + int ret = -1; + bool can_readpixels = true; + + can_readpixels = vrend_format_can_render(res->base.format) || vrend_format_is_ds(res->base.format); + + if (can_readpixels) + ret = vrend_transfer_send_readpixels(res, iov, num_iovs, info); + + /* Can hit this on a non-error path as well. */ + if (ret) + ret = vrend_transfer_send_readonly(res, iov, num_iovs, info); + + return ret; + } + return 0; +} + +int vrend_renderer_transfer_iov(struct virgl_client *client, const struct vrend_transfer_info *info, int transfer_mode) +{ + struct vrend_resource *res; + struct vrend_context *ctx; + struct iovec *iov; + int num_iovs; + + if (!info->box) + return EINVAL; + + ctx = vrend_lookup_renderer_ctx(client, info->ctx_id); + if (!ctx) + return EINVAL; + + if (info->ctx_id == 0) + res = vrend_resource_lookup(client, info->handle, 0); + else + res = vrend_renderer_ctx_res_lookup(ctx, info->handle); + + if (!res) + return EINVAL; + + iov = info->iovec; + num_iovs = info->iovec_cnt; + + if (res->iov && (!iov || num_iovs == 0)) { + iov = res->iov; + num_iovs = res->num_iovs; + } + + if (!iov) + return EINVAL; + + if (!check_transfer_bounds(res, info)) + return EINVAL; + + if (!check_iov_bounds(res, info, iov, num_iovs)) + return EINVAL; + + if (info->context0) { + vrend_renderer_force_ctx_0(client); + ctx = NULL; + } + + switch (transfer_mode) { + case VIRGL_TRANSFER_TO_HOST: + return vrend_renderer_transfer_write_iov(ctx, res, iov, num_iovs, info); + case VIRGL_TRANSFER_FROM_HOST: + return vrend_renderer_transfer_send_iov(res, iov, num_iovs, info); + + default: + assert(0); + } + return 0; +} + +int vrend_transfer_inline_write(struct vrend_context *ctx, + struct vrend_transfer_info *info) +{ + struct vrend_resource *res; + + res = vrend_renderer_ctx_res_lookup(ctx, info->handle); + if (!res) + return EINVAL; + + if (!check_transfer_bounds(res, info)) + return EINVAL; + + if (!check_iov_bounds(res, info, info->iovec, info->iovec_cnt)) + return EINVAL; + + return vrend_renderer_transfer_write_iov(ctx, res, info->iovec, info->iovec_cnt, info); + +} + +int vrend_renderer_copy_transfer3d(struct vrend_context *ctx, + struct vrend_transfer_info *info, + uint32_t src_handle) +{ + struct vrend_resource *src_res, *dst_res; + + src_res = vrend_renderer_ctx_res_lookup(ctx, src_handle); + dst_res = vrend_renderer_ctx_res_lookup(ctx, info->handle); + + if (!src_res) + return EINVAL; + + if (!dst_res) + return EINVAL; + + if (!src_res->iov) + return EINVAL; + + if (!check_transfer_bounds(dst_res, info)) + return EINVAL; + + if (!check_iov_bounds(dst_res, info, src_res->iov, src_res->num_iovs)) + return EINVAL; + + return vrend_renderer_transfer_write_iov(ctx, dst_res, src_res->iov, + src_res->num_iovs, info); +} + +void vrend_set_stencil_ref(struct vrend_context *ctx, + struct pipe_stencil_ref *ref) +{ + if (ctx->sub->stencil_refs[0] != ref->ref_value[0] || + ctx->sub->stencil_refs[1] != ref->ref_value[1]) { + ctx->sub->stencil_refs[0] = ref->ref_value[0]; + ctx->sub->stencil_refs[1] = ref->ref_value[1]; + ctx->sub->stencil_state_dirty = true; + } +} + +void vrend_set_blend_color(struct vrend_context *ctx, + struct pipe_blend_color *color) +{ + ctx->sub->blend_color = *color; + glBlendColor(color->color[0], color->color[1], color->color[2], + color->color[3]); +} + +void vrend_set_scissor_state(struct vrend_context *ctx, + uint32_t start_slot, + uint32_t num_scissor, + struct pipe_scissor_state *ss) +{ + uint i, idx; + + if (start_slot > PIPE_MAX_VIEWPORTS || + num_scissor > (PIPE_MAX_VIEWPORTS - start_slot)) + return; + + for (i = 0; i < num_scissor; i++) { + idx = start_slot + i; + ctx->sub->ss[idx] = ss[i]; + ctx->sub->scissor_state_dirty |= (1 << idx); + } +} + +void vrend_set_polygon_stipple(struct vrend_context *ctx, + struct pipe_poly_stipple *ps) +{ + static const unsigned bit31 = 1u << 31; + GLubyte *stip = calloc(1, 1024); + int i, j; + + if (!ctx->pstip_inited) + vrend_init_pstipple_texture(ctx); + + if (!stip) + return; + + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + if (ps->stipple[i] & (bit31 >> j)) + stip[i * 32 + j] = 0; + else + stip[i * 32 + j] = 255; + } + } + + glBindTexture(GL_TEXTURE_2D, ctx->pstipple_tex_id); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 32, 32, + GL_RED, GL_UNSIGNED_BYTE, stip); + glBindTexture(GL_TEXTURE_2D, 0); + + free(stip); +} + +void vrend_set_clip_state(struct vrend_context *ctx, struct pipe_clip_state *ucp) +{ + ctx->sub->ucp_state = *ucp; +} + +void vrend_set_sample_mask(UNUSED struct vrend_context *ctx, unsigned sample_mask) +{ + if (has_feature(feat_sample_mask)) + glSampleMaski(0, sample_mask); +} + +void vrend_set_min_samples(struct vrend_context *ctx, unsigned min_samples) +{ + float min_sample_shading = (float)min_samples; + if (ctx->sub->nr_cbufs > 0 && ctx->sub->surf[0]) { + assert(ctx->sub->surf[0]->texture); + min_sample_shading /= MAX2(1, ctx->sub->surf[0]->texture->base.nr_samples); + } + + if (has_feature(feat_sample_shading)) + glMinSampleShading(min_sample_shading); +} + +void vrend_set_tess_state(UNUSED struct vrend_context *ctx, const float tess_factors[6]) +{ + if (has_feature(feat_tessellation)) + memcpy(ctx->client->vrend_state->tess_factors, tess_factors, 6 * sizeof (float)); +} + +static void vrend_hw_emit_streamout_targets(UNUSED struct vrend_context *ctx, struct vrend_streamout_object *so_obj) +{ + uint i; + + for (i = 0; i < so_obj->num_targets; i++) { + if (!so_obj->so_targets[i]) + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, 0); + else if (so_obj->so_targets[i]->buffer_offset || so_obj->so_targets[i]->buffer_size < so_obj->so_targets[i]->buffer->base.width0) + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, i, so_obj->so_targets[i]->buffer->id, so_obj->so_targets[i]->buffer_offset, so_obj->so_targets[i]->buffer_size); + else + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, so_obj->so_targets[i]->buffer->id); + } +} + +void vrend_set_streamout_targets(struct vrend_context *ctx, + UNUSED uint32_t append_bitmask, + uint32_t num_targets, + uint32_t *handles) +{ + struct vrend_so_target *target; + uint i; + + if (!has_feature(feat_transform_feedback)) + return; + + if (num_targets) { + bool found = false; + struct vrend_streamout_object *obj; + LIST_FOR_EACH_ENTRY(obj, &ctx->sub->streamout_list, head) { + if (obj->num_targets == num_targets) { + if (!memcmp(handles, obj->handles, num_targets * 4)) { + found = true; + break; + } + } + } + if (found) { + ctx->sub->current_so = obj; + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id); + return; + } + + obj = CALLOC_STRUCT(vrend_streamout_object); + if (has_feature(feat_transform_feedback2)) { + glGenTransformFeedbacks(1, &obj->id); + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id); + } + obj->num_targets = num_targets; + for (i = 0; i < num_targets; i++) { + obj->handles[i] = handles[i]; + if (handles[i] == 0) + continue; + target = vrend_object_lookup(ctx->sub->object_hash, handles[i], VIRGL_OBJECT_STREAMOUT_TARGET); + if (!target) { + free(obj); + return; + } + vrend_so_target_reference(&obj->so_targets[i], target); + } + vrend_hw_emit_streamout_targets(ctx, obj); + list_addtail(&obj->head, &ctx->sub->streamout_list); + ctx->sub->current_so = obj; + obj->xfb_state = XFB_STATE_STARTED_NEED_BEGIN; + } else { + if (has_feature(feat_transform_feedback2)) + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); + ctx->sub->current_so = NULL; + } +} + +static void vrend_resource_buffer_copy(UNUSED struct vrend_context *ctx, + struct vrend_resource *src_res, + struct vrend_resource *dst_res, + uint32_t dstx, uint32_t srcx, + uint32_t width) +{ + glBindBuffer(GL_COPY_READ_BUFFER, src_res->id); + glBindBuffer(GL_COPY_WRITE_BUFFER, dst_res->id); + + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, srcx, dstx, width); + glBindBuffer(GL_COPY_READ_BUFFER, 0); + glBindBuffer(GL_COPY_WRITE_BUFFER, 0); +} + +static void vrend_resource_copy_fallback(struct vrend_resource *src_res, + struct vrend_resource *dst_res, + uint32_t dst_level, + uint32_t dstx, uint32_t dsty, + uint32_t dstz, uint32_t src_level, + const struct pipe_box *src_box) +{ + char *tptr; + uint32_t total_size, src_stride, dst_stride, src_layer_stride; + GLenum glformat, gltype; + int elsize = util_format_get_blocksize(dst_res->base.format); + int compressed = util_format_is_compressed(dst_res->base.format); + int cube_slice = 1; + uint32_t slice_size, slice_offset; + int i; + struct pipe_box box; + + if (src_res->target == GL_TEXTURE_CUBE_MAP) + cube_slice = 6; + + if (src_res->base.format != dst_res->base.format) + return; + + box = *src_box; + box.depth = vrend_get_texture_depth(src_res, src_level); + dst_stride = util_format_get_stride(dst_res->base.format, dst_res->base.width0); + + /* this is ugly need to do a full GetTexImage */ + slice_size = util_format_get_nblocks(src_res->base.format, u_minify(src_res->base.width0, src_level), u_minify(src_res->base.height0, src_level)) * + util_format_get_blocksize(src_res->base.format); + total_size = slice_size * vrend_get_texture_depth(src_res, src_level); + + tptr = malloc(total_size); + if (!tptr) + return; + + glformat = tex_conv_table[src_res->base.format].glformat; + gltype = tex_conv_table[src_res->base.format].gltype; + + if (compressed) + glformat = tex_conv_table[src_res->base.format].internalformat; + + /* If we are on gles we need to rely on the textures backing + * iovec to have the data we need, otherwise we can use glGetTexture + */ + uint64_t src_offset = 0; + uint64_t dst_offset = 0; + if (src_level < VR_MAX_TEXTURE_2D_LEVELS) { + src_offset = src_res->mipmap_offsets[src_level]; + dst_offset = dst_res->mipmap_offsets[src_level]; + } + + src_stride = util_format_get_nblocksx(src_res->base.format, + u_minify(src_res->base.width0, src_level)) * elsize; + src_layer_stride = util_format_get_2d_size(src_res->base.format, + src_stride, + u_minify(src_res->base.height0, src_level)); + read_transfer_data(src_res->iov, src_res->num_iovs, tptr, + src_res->base.format, src_offset, + src_stride, src_layer_stride, &box, false); + /* When on GLES sync the iov that backs the dst resource because + * we might need it in a chain copy A->B, B->C */ + write_transfer_data(&dst_res->base, dst_res->iov, dst_res->num_iovs, tptr, + dst_stride, &box, src_level, dst_offset, false); + /* we get values from the guest as 24-bit scaled integers + but we give them to the host GL and it interprets them + as 32-bit scaled integers, so we need to scale them here */ + if (dst_res->base.format == VIRGL_FORMAT_Z24X8_UNORM) { + float depth_scale = 256.0; + vrend_scale_depth(tptr, total_size, depth_scale); + } + + glPixelStorei(GL_PACK_ALIGNMENT, 4); + switch (elsize) { + case 1: + case 3: + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + break; + case 2: + case 6: + glPixelStorei(GL_UNPACK_ALIGNMENT, 2); + break; + case 4: + default: + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + break; + case 8: + glPixelStorei(GL_UNPACK_ALIGNMENT, 8); + break; + } + + glBindTexture(dst_res->target, dst_res->id); + slice_offset = src_box->z * slice_size; + cube_slice = (src_res->target == GL_TEXTURE_CUBE_MAP) ? src_box->z + src_box->depth : cube_slice; + i = (src_res->target == GL_TEXTURE_CUBE_MAP) ? src_box->z : 0; + for (; i < cube_slice; i++) { + GLenum ctarget = dst_res->target == GL_TEXTURE_CUBE_MAP ? + (GLenum)(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) : dst_res->target; + if (compressed) { + glCompressedTexSubImage2D(ctarget, dst_level, dstx, dsty, + src_box->width, src_box->height, + glformat, slice_size, tptr + slice_offset); + } else { + if (ctarget == GL_TEXTURE_3D || + ctarget == GL_TEXTURE_2D_ARRAY || + ctarget == GL_TEXTURE_CUBE_MAP_ARRAY) { + glTexSubImage3D(ctarget, dst_level, dstx, dsty, dstz, src_box->width, src_box->height, src_box->depth, glformat, gltype, tptr + slice_offset); + } else { + glTexSubImage2D(ctarget, dst_level, dstx, dsty, src_box->width, src_box->height, glformat, gltype, tptr + slice_offset); + } + } + slice_offset += slice_size; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + free(tptr); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static inline void +vrend_copy_sub_image(struct vrend_resource* src_res, struct vrend_resource * dst_res, + uint32_t src_level, const struct pipe_box *src_box, + uint32_t dst_level, uint32_t dstx, uint32_t dsty, uint32_t dstz) +{ + + GLenum src_target = tgsitargettogltarget(src_res->base.target, src_res->base.nr_samples); + GLenum dst_target = tgsitargettogltarget(dst_res->base.target, dst_res->base.nr_samples); + + src_target = translate_gles_emulation_texture_target(src_target); + dst_target = translate_gles_emulation_texture_target(dst_target); + + glCopyImageSubData(src_res->id, src_target, src_level, + src_box->x, src_box->y, src_box->z, + dst_res->id, dst_target, dst_level, + dstx, dsty, dstz, + src_box->width, src_box->height,src_box->depth); +} + + +void vrend_renderer_resource_copy_region(struct vrend_context *ctx, + uint32_t dst_handle, uint32_t dst_level, + uint32_t dstx, uint32_t dsty, uint32_t dstz, + uint32_t src_handle, uint32_t src_level, + const struct pipe_box *src_box) +{ + struct vrend_resource *src_res, *dst_res; + GLbitfield glmask = 0; + GLint sy1, sy2, dy1, dy2; + + if (ctx->in_error) + return; + + src_res = vrend_renderer_ctx_res_lookup(ctx, src_handle); + dst_res = vrend_renderer_ctx_res_lookup(ctx, dst_handle); + + if (!src_res) + return; + if (!dst_res) + return; + + if (src_res->base.target == PIPE_BUFFER && dst_res->base.target == PIPE_BUFFER) { + /* do a buffer copy */ + vrend_resource_buffer_copy(ctx, src_res, dst_res, dstx, + src_box->x, src_box->width); + return; + } + + if (has_feature(feat_copy_image) && + format_is_copy_compatible(src_res->base.format,dst_res->base.format, true) && + src_res->base.nr_samples == dst_res->base.nr_samples) { + vrend_copy_sub_image(src_res, dst_res, src_level, src_box, + dst_level, dstx, dsty, dstz); + return; + } + + if (!vrend_format_can_render(src_res->base.format) || + !vrend_format_can_render(dst_res->base.format)) { + vrend_resource_copy_fallback(src_res, dst_res, dst_level, dstx, + dsty, dstz, src_level, src_box); + return; + } + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + + /* clean out fb ids */ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + vrend_fb_bind_texture(src_res, 0, src_level, src_box->z); + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + vrend_fb_bind_texture(dst_res, 0, dst_level, dstz); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + + glmask = GL_COLOR_BUFFER_BIT; + glDisable(GL_SCISSOR_TEST); + + if (!src_res->y_0_top) { + sy1 = src_box->y; + sy2 = src_box->y + src_box->height; + } else { + sy1 = src_res->base.height0 - src_box->y - src_box->height; + sy2 = src_res->base.height0 - src_box->y; + } + + if (!dst_res->y_0_top) { + dy1 = dsty; + dy2 = dsty + src_box->height; + } else { + dy1 = dst_res->base.height0 - dsty - src_box->height; + dy2 = dst_res->base.height0 - dsty; + } + + glBlitFramebuffer(src_box->x, sy1, + src_box->x + src_box->width, + sy2, + dstx, dy1, + dstx + src_box->width, + dy2, + glmask, GL_NEAREST); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->fb_id); + + if (ctx->sub->rs_state.scissor) + glEnable(GL_SCISSOR_TEST); +} + +static void vrend_renderer_blit_int(struct vrend_context *ctx, + struct vrend_resource *src_res, + struct vrend_resource *dst_res, + const struct pipe_blit_info *info) +{ + GLbitfield glmask = 0; + int src_y1, src_y2, dst_y1, dst_y2; + GLenum filter; + int n_layers = 1, i; + bool use_gl = false; + bool make_intermediate_copy = false; + bool skip_dest_swizzle = false; + GLuint intermediate_fbo = 0; + struct vrend_resource *intermediate_copy = 0; + + GLuint blitter_views[2] = {src_res->id, dst_res->id}; + + filter = convert_mag_filter(info->filter); + + /* if we can't make FBO's use the fallback path */ + if (!vrend_format_can_render(src_res->base.format) && + !vrend_format_is_ds(src_res->base.format)) + use_gl = true; + if (!vrend_format_can_render(dst_res->base.format) && + !vrend_format_is_ds(dst_res->base.format)) + use_gl = true; + + /* different depth formats */ + if (vrend_format_is_ds(src_res->base.format) && + vrend_format_is_ds(dst_res->base.format)) { + if (src_res->base.format != dst_res->base.format) { + if (!(src_res->base.format == PIPE_FORMAT_S8_UINT_Z24_UNORM && + (dst_res->base.format == PIPE_FORMAT_Z24X8_UNORM))) { + use_gl = true; + } + } + } + /* glBlitFramebuffer - can support depth stencil with NEAREST + which we use for mipmaps */ + if ((info->mask & (PIPE_MASK_Z | PIPE_MASK_S)) && info->filter == PIPE_TEX_FILTER_LINEAR) + use_gl = true; + + /* for scaled MS blits we either need extensions or hand roll */ + if (info->mask & PIPE_MASK_RGBA && + src_res->base.nr_samples > 0 && + src_res->base.nr_samples != dst_res->base.nr_samples && + (info->src.box.width != info->dst.box.width || + info->src.box.height != info->dst.box.height)) { + use_gl = true; + } + + if (!dst_res->y_0_top) { + dst_y1 = info->dst.box.y + info->dst.box.height; + dst_y2 = info->dst.box.y; + } else { + dst_y1 = dst_res->base.height0 - info->dst.box.y - info->dst.box.height; + dst_y2 = dst_res->base.height0 - info->dst.box.y; + } + + if (!src_res->y_0_top) { + src_y1 = info->src.box.y + info->src.box.height; + src_y2 = info->src.box.y; + } else { + src_y1 = src_res->base.height0 - info->src.box.y - info->src.box.height; + src_y2 = src_res->base.height0 - info->src.box.y; + } + + /* GLES generally doesn't support blitting to a multi-sample FB, and also not + * from a multi-sample FB where the regions are not exatly the same or the + * source and target format are different. For + * downsampling DS blits to zero samples we solve this by doing two blits */ + if (((dst_res->base.nr_samples > 0) || + ((info->mask & PIPE_MASK_RGBA) && + (src_res->base.nr_samples > 0) && + (info->src.box.x != info->dst.box.x || + info->src.box.width != info->dst.box.width || + dst_y1 != src_y1 || dst_y2 != src_y2 || + info->src.format != info->dst.format)) + )) + use_gl = true; + + /* for 3D mipmapped blits - hand roll time */ + if (info->src.box.depth != info->dst.box.depth) + use_gl = true; + + if (vrend_blit_needs_swizzle(info->dst.format, info->src.format)) { + use_gl = true; + + if (dst_res->base.bind & VIRGL_BIND_PREFER_EMULATED_BGRA) + skip_dest_swizzle = true; + } + + if (use_gl) { + vrend_renderer_blit_gl(ctx->client, src_res, dst_res, blitter_views, info, + has_feature(feat_texture_srgb_decode), + has_feature(feat_srgb_write_control), + skip_dest_swizzle); + vrend_clicbs->make_current(ctx->client, ctx->sub->gl_context); + goto cleanup; + } + + if (info->mask & PIPE_MASK_Z) + glmask |= GL_DEPTH_BUFFER_BIT; + if (info->mask & PIPE_MASK_S) + glmask |= GL_STENCIL_BUFFER_BIT; + if (info->mask & PIPE_MASK_RGBA) + glmask |= GL_COLOR_BUFFER_BIT; + + if (info->scissor_enable) { + glScissor(info->scissor.minx, info->scissor.miny, info->scissor.maxx - info->scissor.minx, info->scissor.maxy - info->scissor.miny); + ctx->sub->scissor_state_dirty = (1 << 0); + glEnable(GL_SCISSOR_TEST); + } else + glDisable(GL_SCISSOR_TEST); + + /* An GLES GL_INVALID_OPERATION is generated if one wants to blit from a + * multi-sample fbo to a non multi-sample fbo and the source and destination + * rectangles are not defined with the same (X0, Y0) and (X1, Y1) bounds. + * + * Since stencil data can only be written in a fragment shader when + * ARB_shader_stencil_export is available, the workaround using GL as given + * above is usually not available. Instead, to work around the blit + * limitations on GLES first copy the full frame to a non-multisample + * surface and then copy the according area to the final target surface. + */ + if ((info->mask & PIPE_MASK_ZS) && + ((src_res->base.nr_samples > 0) && + (src_res->base.nr_samples != dst_res->base.nr_samples)) && + ((info->src.box.x != info->dst.box.x) || + (src_y1 != dst_y1) || + (info->src.box.width != info->dst.box.width) || + (src_y2 != dst_y2))) { + + make_intermediate_copy = true; + + /* Create a texture that is the same like the src_res texture, but + * without multi-sample */ + struct vrend_renderer_resource_create_args args; + memset(&args, 0, sizeof(struct vrend_renderer_resource_create_args)); + args.width = src_res->base.width0; + args.height = src_res->base.height0; + args.depth = src_res->base.depth0; + args.format = info->src.format; + args.target = src_res->base.target; + args.last_level = src_res->base.last_level; + args.array_size = src_res->base.array_size; + intermediate_copy = (struct vrend_resource *)CALLOC_STRUCT(vrend_texture); + vrend_renderer_resource_copy_args(&args, intermediate_copy); + MAYBE_UNUSED int r = vrend_renderer_resource_allocate_texture(intermediate_copy); + assert(!r); + + glGenFramebuffers(1, &intermediate_fbo); + } else { + /* If no intermediate copy is needed make the variables point to the + * original source to simplify the code below. + */ + intermediate_fbo = ctx->sub->blit_fb_ids[0]; + intermediate_copy = src_res; + } + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + if (info->mask & PIPE_MASK_RGBA) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + else + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + if (info->mask & PIPE_MASK_RGBA) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + else if (info->mask & (PIPE_MASK_Z | PIPE_MASK_S)) + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + if (info->src.box.depth == info->dst.box.depth) + n_layers = info->dst.box.depth; + for (i = 0; i < n_layers; i++) { + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + vrend_fb_bind_texture_id(src_res, blitter_views[0], 0, info->src.level, info->src.box.z + i); + + if (make_intermediate_copy) { + int level_width = u_minify(src_res->base.width0, info->src.level); + int level_height = u_minify(src_res->base.width0, info->src.level); + glBindFramebuffer(GL_FRAMEBUFFER, intermediate_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + vrend_fb_bind_texture(intermediate_copy, 0, info->src.level, info->src.box.z + i); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediate_fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + glBlitFramebuffer(0, 0, level_width, level_height, + 0, 0, level_width, level_height, + glmask, filter); + } + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + vrend_fb_bind_texture_id(dst_res, blitter_views[1], 0, info->dst.level, info->dst.box.z + i); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_fbo); + + glBlitFramebuffer(info->src.box.x, + src_y1, + info->src.box.x + info->src.box.width, + src_y2, + info->dst.box.x, + dst_y1, + info->dst.box.x + info->dst.box.width, + dst_y2, + glmask, filter); + } + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, 0, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_TEXTURE_2D, 0, 0); + + glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->fb_id); + + if (make_intermediate_copy) { + vrend_renderer_resource_destroy(intermediate_copy); + glDeleteFramebuffers(1, &intermediate_fbo); + } + + if (ctx->sub->rs_state.scissor) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + +cleanup: + if (blitter_views[0] != src_res->id) + glDeleteTextures(1, &blitter_views[0]); + + if (blitter_views[1] != dst_res->id) + glDeleteTextures(1, &blitter_views[1]); +} + +void vrend_renderer_blit(struct vrend_context *ctx, + uint32_t dst_handle, uint32_t src_handle, + const struct pipe_blit_info *info) +{ + struct vrend_resource *src_res, *dst_res; + src_res = vrend_renderer_ctx_res_lookup(ctx, src_handle); + dst_res = vrend_renderer_ctx_res_lookup(ctx, dst_handle); + + if (!src_res) + return; + if (!dst_res) + return; + + if (ctx->in_error) + return; + + if (!info->src.format || info->src.format >= VIRGL_FORMAT_MAX) + return; + + if (!info->dst.format || info->dst.format >= VIRGL_FORMAT_MAX) + return; + + /* The Gallium blit function can be called for a general blit that may + * scale, convert the data, and apply some rander states, or it is called via + * glCopyImageSubData. If the src or the dst image are equal, or the two + * images formats are the same, then Galliums such calles are redirected + * to resource_copy_region, in this case and if no render states etx need + * to be applied, forward the call to glCopyImageSubData, otherwise do a + * normal blit. */ + if (has_feature(feat_copy_image) && + (!info->render_condition_enable || !ctx->sub->cond_render_gl_mode) && + format_is_copy_compatible(info->src.format,info->dst.format, false) && + !info->scissor_enable && (info->filter == PIPE_TEX_FILTER_NEAREST) && + !info->alpha_blend && (info->mask == PIPE_MASK_RGBA) && + src_res->base.nr_samples == dst_res->base.nr_samples && + info->src.box.width == info->dst.box.width && + info->src.box.height == info->dst.box.height && + info->src.box.depth == info->dst.box.depth) { + vrend_copy_sub_image(src_res, dst_res, info->src.level, &info->src.box, + info->dst.level, info->dst.box.x, info->dst.box.y, + info->dst.box.z); + } else { + vrend_renderer_blit_int(ctx, src_res, dst_res, info); + } +} + +int vrend_renderer_create_fence(struct virgl_client *client, int client_fence_id, uint32_t ctx_id) +{ + struct vrend_fence *fence; + + fence = malloc(sizeof(struct vrend_fence)); + if (!fence) + return ENOMEM; + + fence->ctx_id = ctx_id; + fence->fence_id = client_fence_id; + fence->syncobj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + + if (fence->syncobj == NULL) + goto fail; + + list_addtail(&fence->fences, &client->vrend_state->fence_list); + return 0; + + fail: + free(fence); + return ENOMEM; +} + +static void free_fence_locked(struct vrend_fence *fence) +{ + list_del(&fence->fences); + glDeleteSync(fence->syncobj); + free(fence); +} + +static void vrend_renderer_check_queries(struct virgl_client *client); + +void vrend_renderer_check_fences(struct virgl_client *client) +{ + struct vrend_fence *fence, *stor; + uint32_t latest_id = 0; + GLenum glret; + + vrend_renderer_force_ctx_0(client); + + LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &client->vrend_state->fence_list, fences) { + glret = glClientWaitSync(fence->syncobj, 0, 0); + if (glret == GL_ALREADY_SIGNALED){ + latest_id = fence->fence_id; + free_fence_locked(fence); + } + /* don't bother checking any subsequent ones */ + else if (glret == GL_TIMEOUT_EXPIRED) { + break; + } + } + + if (latest_id == 0) + return; + + vrend_renderer_check_queries(client); + + vrend_clicbs->write_fence(client, latest_id); +} + +static bool vrend_get_one_query_result(GLuint query_id, uint64_t *result) +{ + GLuint ready; + GLuint passed; + GLuint64 pass64; + + glGetQueryObjectuiv(query_id, GL_QUERY_RESULT_AVAILABLE, &ready); + + if (!ready) + return false; + + glGetQueryObjectuiv(query_id, GL_QUERY_RESULT, &passed); + *result = passed; + return true; +} + +static bool vrend_check_query(struct vrend_query *query) +{ + struct virgl_host_query_state state; + bool ret; + + state.result_size = 4; + ret = vrend_get_one_query_result(query->id, &state.result); + if (ret == false) + return false; + + /* We got a boolean, but the client wanted the actual number of samples + * blow the number up so that the client doesn't think it was just one pixel + * and discards an object that might be bigger */ + if (query->fake_samples_passed) + state.result *= fake_occlusion_query_samples_passed_default; + + state.query_state = VIRGL_QUERY_STATE_DONE; + + if (query->res->iov) { + vrend_write_to_iovec(query->res->iov, query->res->num_iovs, 0, + (const void *) &state, sizeof(state)); + } else { + *((struct virgl_host_query_state *) query->res->ptr) = state; + } + + return true; +} + +static void vrend_renderer_check_queries(struct virgl_client *client) +{ + struct vrend_query *query, *stor; + + LIST_FOR_EACH_ENTRY_SAFE(query, stor, &client->vrend_state->waiting_query_list, waiting_queries) { + vrend_hw_switch_context(vrend_lookup_renderer_ctx(client, query->ctx_id), true); + if (vrend_check_query(query)) + list_delinit(&query->waiting_queries); + } +} + +bool vrend_hw_switch_context(struct vrend_context *ctx, bool now) +{ + if (!ctx) + return false; + + if (ctx == ctx->client->vrend_state->current_ctx && ctx->ctx_switch_pending == false) + return true; + + if (ctx->ctx_id != 0 && ctx->in_error) + return false; + + ctx->ctx_switch_pending = true; + if (now) + vrend_finish_context_switch(ctx); + + ctx->client->vrend_state->current_ctx = ctx; + return true; +} + +static void vrend_finish_context_switch(struct vrend_context *ctx) +{ + if (ctx->ctx_switch_pending == false) + return; + ctx->ctx_switch_pending = false; + + if (ctx->client->vrend_state->current_hw_ctx == ctx) + return; + + ctx->client->vrend_state->current_hw_ctx = ctx; + + vrend_clicbs->make_current(ctx->client, ctx->sub->gl_context); +} + +void +vrend_renderer_object_destroy(struct vrend_context *ctx, uint32_t handle) +{ + vrend_object_remove(ctx->sub->object_hash, handle, 0); +} + +uint32_t vrend_renderer_object_insert(struct vrend_context *ctx, void *data, + uint32_t size, uint32_t handle, enum virgl_object_type type) +{ + return vrend_object_insert(ctx->sub->object_hash, data, size, handle, type); +} + +int vrend_create_query(struct vrend_context *ctx, uint32_t handle, + uint32_t query_type, uint32_t query_index, + uint32_t res_handle, UNUSED uint32_t offset) +{ + struct vrend_query *q; + struct vrend_resource *res; + uint32_t ret_handle; + bool fake_samples_passed = false; + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + if (!res || !has_bit(res->storage_bits, VREND_STORAGE_HOST_SYSTEM_MEMORY)) + return EINVAL; + + /* If we don't have ARB_occlusion_query, at least try to fake GL_SAMPLES_PASSED + * by using GL_ANY_SAMPLES_PASSED (i.e. EXT_occlusion_query_boolean) */ + if (query_type == PIPE_QUERY_OCCLUSION_COUNTER) { + query_type = PIPE_QUERY_OCCLUSION_PREDICATE; + fake_samples_passed = true; + } + + if (query_type == PIPE_QUERY_OCCLUSION_PREDICATE && + !has_feature(feat_occlusion_query_boolean)) + return EINVAL; + + q = CALLOC_STRUCT(vrend_query); + if (!q) + return ENOMEM; + + list_inithead(&q->waiting_queries); + q->type = query_type; + q->index = query_index; + q->ctx_id = ctx->ctx_id; + q->fake_samples_passed = fake_samples_passed; + + vrend_resource_reference(&q->res, res); + + switch (q->type) { + case PIPE_QUERY_OCCLUSION_COUNTER: + return EINVAL; + case PIPE_QUERY_OCCLUSION_PREDICATE: + if (has_feature(feat_occlusion_query_boolean)) { + q->gltype = GL_ANY_SAMPLES_PASSED; + break; + } else + return EINVAL; + case PIPE_QUERY_TIMESTAMP: + return EINVAL; + case PIPE_QUERY_TIME_ELAPSED: + return EINVAL; + case PIPE_QUERY_PRIMITIVES_GENERATED: + q->gltype = GL_PRIMITIVES_GENERATED; + break; + case PIPE_QUERY_PRIMITIVES_EMITTED: + q->gltype = GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN; + break; + case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE: + q->gltype = GL_ANY_SAMPLES_PASSED_CONSERVATIVE; + break; + case PIPE_QUERY_SO_OVERFLOW_PREDICATE: + return EINVAL; + case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE: + return EINVAL; + } + + glGenQueries(1, &q->id); + + ret_handle = vrend_renderer_object_insert(ctx, q, sizeof(struct vrend_query), handle, + VIRGL_OBJECT_QUERY); + if (!ret_handle) { + FREE(q); + return ENOMEM; + } + return 0; +} + +static void vrend_destroy_query(struct vrend_query *query) +{ + vrend_resource_reference(&query->res, NULL); + list_del(&query->waiting_queries); + glDeleteQueries(1, &query->id); + free(query); +} + +static void vrend_destroy_query_object(void *obj_ptr) +{ + struct vrend_query *query = obj_ptr; + vrend_destroy_query(query); +} + +int vrend_begin_query(struct vrend_context *ctx, uint32_t handle) +{ + struct vrend_query *q; + + q = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_QUERY); + if (!q) + return EINVAL; + + if (q->index > 0) + return EINVAL; + + list_delinit(&q->waiting_queries); + + glBeginQuery(q->gltype, q->id); + return 0; +} + +int vrend_end_query(struct vrend_context *ctx, uint32_t handle) +{ + struct vrend_query *q; + q = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_QUERY); + if (!q) + return EINVAL; + + if (q->index > 0) + return EINVAL; + + glEndQuery(q->gltype); + return 0; +} + +void vrend_get_query_result(struct vrend_context *ctx, uint32_t handle, + UNUSED uint32_t wait) +{ + struct vrend_query *q; + bool ret; + + q = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_QUERY); + if (!q) + return; + + ret = vrend_check_query(q); + if (ret) { + list_delinit(&q->waiting_queries); + } else if (LIST_IS_EMPTY(&q->waiting_queries)) { + list_addtail(&q->waiting_queries, &ctx->client->vrend_state->waiting_query_list); + } +} + +int vrend_create_so_target(struct vrend_context *ctx, + uint32_t handle, + uint32_t res_handle, + uint32_t buffer_offset, + uint32_t buffer_size) +{ + struct vrend_so_target *target; + struct vrend_resource *res; + int ret_handle; + res = vrend_renderer_ctx_res_lookup(ctx, res_handle); + if (!res) + return EINVAL; + + target = CALLOC_STRUCT(vrend_so_target); + if (!target) + return ENOMEM; + + pipe_reference_init(&target->reference, 1); + target->res_handle = res_handle; + target->buffer_offset = buffer_offset; + target->buffer_size = buffer_size; + target->sub_ctx = ctx->sub; + vrend_resource_reference(&target->buffer, res); + + ret_handle = vrend_renderer_object_insert(ctx, target, sizeof(*target), handle, + VIRGL_OBJECT_STREAMOUT_TARGET); + if (ret_handle == 0) { + FREE(target); + return ENOMEM; + } + return 0; +} + +static void vrend_fill_caps_glsl_version(int gles_ver, union virgl_caps *caps) +{ + if (gles_ver > 0) { + caps->v1.glsl_level = 120; + + if (gles_ver >= 31) + caps->v1.glsl_level = 310; + else if (gles_ver >= 30) + caps->v1.glsl_level = 130; + } + + if (caps->v1.glsl_level < 400) { + if (has_feature(feat_tessellation) && + has_feature(feat_geometry_shader) && + has_feature(feat_gpu_shader5)) { + /* This is probably a lie, but Gallium enables + * OES_geometry_shader and ARB_gpu_shader5 + * based on this value, apart from that it doesn't + * seem to be a crucial value */ + caps->v1.glsl_level = 400; + + /* Let's lie a bit more */ + if (has_feature(feat_separate_shader_objects)) { + caps->v1.glsl_level = 410; + + /* Compute shaders require GLSL 4.30 unless the shader explicitely + * specifies GL_ARB_compute_shader as required. However, on OpenGL ES + * they are already supported with version 3.10, so if we already + * advertise a feature level of 410, just lie a bit more to make + * compute shaders available to GL programs that don't specify the + * extension within the shaders. */ + if (has_feature(feat_compute_shader)) + caps->v1.glsl_level = 430; + } + } + } +} + +static void set_format_bit(struct virgl_supported_format_mask *mask, enum virgl_formats fmt) +{ + assert(fmt < VIRGL_FORMAT_MAX); + unsigned val = (unsigned)fmt; + unsigned idx = val / 32; + unsigned bit = val % 32; + assert(idx < ARRAY_SIZE(mask->bitmask)); + mask->bitmask[idx] |= 1u << bit; +} + +/* + * Does all of the common caps setting, + * if it dedects a early out returns true. + */ +static void vrend_renderer_fill_caps_v1(struct virgl_client *client, int gles_ver, union virgl_caps *caps) +{ + int i; + GLint max; + + /* + * We can't fully support this feature on GLES, + * but it is needed for OpenGL 2.1 so lie. + */ + caps->v1.bset.occlusion_query = 1; + + /* Set supported prims here as we now know what shaders we support. */ + caps->v1.prim_mask = (1 << PIPE_PRIM_POINTS) | (1 << PIPE_PRIM_LINES) | + (1 << PIPE_PRIM_LINE_STRIP) | (1 << PIPE_PRIM_LINE_LOOP) | + (1 << PIPE_PRIM_TRIANGLES) | (1 << PIPE_PRIM_TRIANGLE_STRIP) | + (1 << PIPE_PRIM_TRIANGLE_FAN); + + if (caps->v1.glsl_level >= 150) { + caps->v1.prim_mask |= (1 << PIPE_PRIM_LINES_ADJACENCY) | + (1 << PIPE_PRIM_LINE_STRIP_ADJACENCY) | + (1 << PIPE_PRIM_TRIANGLES_ADJACENCY) | + (1 << PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY); + } + if (caps->v1.glsl_level >= 400 || has_feature(feat_tessellation)) + caps->v1.prim_mask |= (1 << PIPE_PRIM_PATCHES); + + if (vrend_has_gl_extension("GL_ARB_vertex_type_10f_11f_11f_rev")) + set_format_bit(&caps->v1.vertexbuffer, VIRGL_FORMAT_R11G11B10_FLOAT); + + if (has_feature(feat_indep_blend)) + caps->v1.bset.indep_blend_enable = 1; + + if (has_feature(feat_draw_instance)) + caps->v1.bset.instanceid = 1; + + if (has_feature(feat_ubo)) { + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_BLOCKS, &max); + caps->v1.max_uniform_blocks = max + 1; + } + + if (vrend_has_gl_extension("GL_ARB_fragment_coord_conventions")) + caps->v1.bset.fragment_coord_conventions = 1; + + if (vrend_has_gl_extension("GL_ARB_seamless_cube_map") || gles_ver >= 30) + caps->v1.bset.seamless_cube_map = 1; + + if (vrend_has_gl_extension("GL_AMD_seamless_cube_map_per_texture")) + caps->v1.bset.seamless_cube_map_per_texture = 1; + + if (has_feature(feat_texture_multisample)) + caps->v1.bset.texture_multisample = 1; + + if (has_feature(feat_tessellation)) + caps->v1.bset.has_tessellation_shaders = 1; + + if (has_feature(feat_sample_shading)) + caps->v1.bset.has_sample_shading = 1; + + if (has_feature(feat_indirect_draw)) + caps->v1.bset.has_indirect_draw = 1; + + if (has_feature(feat_indep_blend_func)) + caps->v1.bset.indep_blend_func = 1; + + if (has_feature(feat_cube_map_array)) + caps->v1.bset.cube_map_array = 1; + + if (vrend_has_gl_extension("GL_ARB_gpu_shader_fp64") && + vrend_has_gl_extension("GL_ARB_gpu_shader5")) + caps->v1.bset.has_fp64 = 1; + + if (vrend_has_gl_extension("GL_ARB_shader_stencil_export")) + caps->v1.bset.shader_stencil_export = 1; + + if (vrend_has_gl_extension("GL_ARB_cull_distance")) + caps->v1.bset.has_cull = 1; + + if (vrend_has_gl_extension("GL_ARB_derivative_control")) + caps->v1.bset.derivative_control = 1; + + if (vrend_has_gl_extension("GL_EXT_texture_mirror_clamp") || + vrend_has_gl_extension("GL_ARB_texture_mirror_clamp_to_edge")) + caps->v1.bset.mirror_clamp = true; + + if (has_feature(feat_texture_array)) { + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &max); + caps->v1.max_texture_array_layers = max; + } + + /* we need tf3 so we can do gallium skip buffers */ + if (has_feature(feat_transform_feedback)) { + if (has_feature(feat_transform_feedback2)) + caps->v1.bset.streamout_pause_resume = 1; + + if (gles_ver > 0) { + glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &max); + /* As with the earlier version of transform feedback this min 4. */ + if (max >= 4) { + caps->v1.max_streamout_buffers = 4; + } + } else + caps->v1.max_streamout_buffers = 4; + } + + if (has_feature(feat_arb_or_gles_ext_texture_buffer)) { + glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &max); + caps->v1.max_tbo_size = max; + } + + if (has_feature(feat_texture_gather)) + caps->v1.max_texture_gather_components = 4; + + caps->v1.max_viewports = 1; + + /* Common limits for all backends. */ + caps->v1.max_render_targets = client->vrend_state->max_draw_buffers; + + glGetIntegerv(GL_MAX_SAMPLES, &max); + caps->v1.max_samples = max; + + /* All of the formats are common. */ + for (i = 0; i < VIRGL_FORMAT_MAX; i++) { + if (tex_conv_table[i].internalformat != 0) { + enum virgl_formats fmt = (enum virgl_formats)i; + if (vrend_format_can_sample(fmt)) { + set_format_bit(&caps->v1.sampler, fmt); + if (vrend_format_can_render(fmt)) + set_format_bit(&caps->v1.render, fmt); + } + } + } +} + +static void vrend_renderer_fill_caps_v2(struct virgl_client *client, int gles_ver, union virgl_caps *caps) +{ + GLint max; + GLfloat range[2]; + + /* Count this up when you add a feature flag that is used to set a CAP in + * the guest that was set unconditionally before. Then check that flag and + * this value to avoid regressions when a guest with a new mesa version is + * run on an old virgl host. Use it also to indicate non-cap fixes on the + * host that help enable features in the guest. */ + caps->v2.host_feature_check_version = 3; + + glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, range); + caps->v2.min_aliased_point_size = range[0]; + caps->v2.max_aliased_point_size = range[1]; + + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, range); + caps->v2.min_aliased_line_width = range[0]; + caps->v2.max_aliased_line_width = range[1]; + + glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &caps->v2.max_texture_lod_bias); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, (GLint*)&caps->v2.max_vertex_attribs); + + if (gles_ver >= 30) + glGetIntegerv(GL_MAX_VERTEX_OUTPUT_COMPONENTS, &max); + else + max = 64; // minimum required value + + caps->v2.max_vertex_outputs = max / 4; + + glGetIntegerv(GL_MIN_PROGRAM_TEXEL_OFFSET, &caps->v2.min_texel_offset); + glGetIntegerv(GL_MAX_PROGRAM_TEXEL_OFFSET, &caps->v2.max_texel_offset); + + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, (GLint*)&caps->v2.uniform_buffer_offset_alignment); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint*)&caps->v2.max_texture_2d_size); + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, (GLint*)&caps->v2.max_texture_3d_size); + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, (GLint*)&caps->v2.max_texture_cube_size); + client->vrend_state->max_texture_2d_size = caps->v2.max_texture_2d_size; + client->vrend_state->max_texture_3d_size = caps->v2.max_texture_3d_size; + client->vrend_state->max_texture_cube_size = caps->v2.max_texture_cube_size; + + if (has_feature(feat_geometry_shader)) { + glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, (GLint*)&caps->v2.max_geom_output_vertices); + glGetIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, (GLint*)&caps->v2.max_geom_total_output_components); + } + + if (has_feature(feat_tessellation)) { + glGetIntegerv(GL_MAX_TESS_PATCH_COMPONENTS, &max); + caps->v2.max_shader_patch_varyings = max / 4; + } else + caps->v2.max_shader_patch_varyings = 0; + + if (has_feature(feat_texture_gather)) { + glGetIntegerv(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET, &caps->v2.min_texture_gather_offset); + glGetIntegerv(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET, &caps->v2.max_texture_gather_offset); + } + + if (has_feature(feat_texture_buffer_range)) { + glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, (GLint*)&caps->v2.texture_buffer_offset_alignment); + } + + if (has_feature(feat_ssbo)) { + glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, (GLint*)&caps->v2.shader_buffer_offset_alignment); + + glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max); + if (max > PIPE_MAX_SHADER_BUFFERS) + max = PIPE_MAX_SHADER_BUFFERS; + caps->v2.max_shader_buffer_other_stages = max; + glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &max); + if (max > PIPE_MAX_SHADER_BUFFERS) + max = PIPE_MAX_SHADER_BUFFERS; + caps->v2.max_shader_buffer_frag_compute = max; + glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, + (GLint*)&caps->v2.max_combined_shader_buffers); + } + + if (has_feature(feat_images)) { + glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &max); + if (max > PIPE_MAX_SHADER_IMAGES) + max = PIPE_MAX_SHADER_IMAGES; + caps->v2.max_shader_image_other_stages = max; + glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &max); + if (max > PIPE_MAX_SHADER_IMAGES) + max = PIPE_MAX_SHADER_IMAGES; + caps->v2.max_shader_image_frag_compute = max; + } + + if (has_feature(feat_storage_multisample)) + caps->v1.max_samples = vrend_renderer_query_multisample_caps(caps->v1.max_samples, &caps->v2); + + caps->v2.capability_bits |= VIRGL_CAP_TGSI_INVARIANT | VIRGL_CAP_SET_MIN_SAMPLES | + VIRGL_CAP_TGSI_PRECISE | VIRGL_CAP_APP_TWEAK_SUPPORT; + + /* If attribute isn't supported, assume 2048 which is the minimum allowed + by the specification. */ + if (gles_ver >= 31) + glGetIntegerv(GL_MAX_VERTEX_ATTRIB_STRIDE, (GLint*)&caps->v2.max_vertex_attrib_stride); + else + caps->v2.max_vertex_attrib_stride = 2048; + + if (has_feature(feat_compute_shader)) { + glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, (GLint*)&caps->v2.max_compute_work_group_invocations); + glGetIntegerv(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, (GLint*)&caps->v2.max_compute_shared_memory_size); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, (GLint*)&caps->v2.max_compute_grid_size[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, (GLint*)&caps->v2.max_compute_grid_size[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, (GLint*)&caps->v2.max_compute_grid_size[2]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, (GLint*)&caps->v2.max_compute_block_size[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, (GLint*)&caps->v2.max_compute_block_size[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, (GLint*)&caps->v2.max_compute_block_size[2]); + + caps->v2.capability_bits |= VIRGL_CAP_COMPUTE_SHADER; + } + + if (has_feature(feat_atomic_counters)) { + glGetIntegerv(GL_MAX_VERTEX_ATOMIC_COUNTERS, + (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_VERTEX)); + glGetIntegerv(GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS, + (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_VERTEX)); + glGetIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTERS, + (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_FRAGMENT)); + glGetIntegerv(GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, + (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_FRAGMENT)); + + if (has_feature(feat_geometry_shader)) { + glGetIntegerv(GL_MAX_GEOMETRY_ATOMIC_COUNTERS, + (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_GEOMETRY)); + glGetIntegerv(GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS, + (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_GEOMETRY)); + } + + if (has_feature(feat_tessellation)) { + glGetIntegerv(GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS, + (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_TESS_CTRL)); + glGetIntegerv(GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS, + (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_TESS_CTRL)); + glGetIntegerv(GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS, + (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_TESS_EVAL)); + glGetIntegerv(GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS, + (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_TESS_EVAL)); + } + + if (has_feature(feat_compute_shader)) { + glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTERS, + (GLint*)(caps->v2.max_atomic_counters + PIPE_SHADER_COMPUTE)); + glGetIntegerv(GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS, + (GLint*)(caps->v2.max_atomic_counter_buffers + PIPE_SHADER_COMPUTE)); + } + + glGetIntegerv(GL_MAX_COMBINED_ATOMIC_COUNTERS, + (GLint*)&caps->v2.max_combined_atomic_counters); + glGetIntegerv(GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS, + (GLint*)&caps->v2.max_combined_atomic_counter_buffers); + } + + if (has_feature(feat_fb_no_attach)) + caps->v2.capability_bits |= VIRGL_CAP_FB_NO_ATTACH; + + if (has_feature(feat_barrier)) + caps->v2.capability_bits |= VIRGL_CAP_MEMORY_BARRIER; + + if (has_feature(feat_copy_image)) + caps->v2.capability_bits |= VIRGL_CAP_COPY_IMAGE; + + if (has_feature(feat_robust_buffer_access)) + caps->v2.capability_bits |= VIRGL_CAP_ROBUST_BUFFER_ACCESS; + + if (has_feature(feat_framebuffer_fetch)) + caps->v2.capability_bits |= VIRGL_CAP_TGSI_FBFETCH; + + if (has_feature(feat_srgb_write_control)) + caps->v2.capability_bits |= VIRGL_CAP_SRGB_WRITE_CONTROL; + + /* always enable, only indicates that the CMD is supported */ + caps->v2.capability_bits |= VIRGL_CAP_GUEST_MAY_INIT_LOG; + + caps->v2.capability_bits |= VIRGL_CAP_TRANSFER; + + if (vrend_check_framebuffer_mixed_color_attachements()) + caps->v2.capability_bits |= VIRGL_CAP_FBO_MIXED_COLOR_FORMATS; + + /* We want to expose ARB_gpu_shader_fp64 when running on top of ES */ + caps->v2.capability_bits |= VIRGL_CAP_FAKE_FP64; + caps->v2.capability_bits |= VIRGL_CAP_BGRA_SRGB_IS_EMULATED; + + if (has_feature(feat_indirect_draw)) + caps->v2.capability_bits |= VIRGL_CAP_BIND_COMMAND_ARGS; + + for (int i = 0; i < VIRGL_FORMAT_MAX; i++) { + enum virgl_formats fmt = (enum virgl_formats)i; + if (tex_conv_table[i].internalformat != 0) { + if (vrend_format_can_readback(fmt)) + set_format_bit(&caps->v2.supported_readback_formats, fmt); + } + + set_format_bit(&caps->v2.scanout, fmt); + } + + if (has_feature(feat_clip_control)) + caps->v2.capability_bits |= VIRGL_CAP_CLIP_HALFZ; + + if (vrend_has_gl_extension("GL_KHR_texture_compression_astc_sliced_3d")) + caps->v2.capability_bits |= VIRGL_CAP_3D_ASTC; + + caps->v2.capability_bits |= VIRGL_CAP_INDIRECT_INPUT_ADDR; + + caps->v2.capability_bits |= VIRGL_CAP_COPY_TRANSFER; +} + +void vrend_renderer_fill_caps(struct virgl_client *client, uint32_t set, UNUSED uint32_t version, union virgl_caps *caps) +{ + int gles_ver; + GLenum err; + bool fill_capset2 = false; + + if (!caps) + return; + + if (set > 2) { + caps->max_version = 0; + return; + } + + if (set == 1) { + memset(caps, 0, sizeof(struct virgl_caps_v1)); + caps->max_version = 1; + } else if (set == 2) { + memset(caps, 0, sizeof(*caps)); + caps->max_version = 2; + fill_capset2 = true; + } + + gles_ver = vrend_gl_version(); + + vrend_fill_caps_glsl_version(gles_ver, caps); + + vrend_renderer_fill_caps_v1(client, gles_ver, caps); + + if (!fill_capset2) + return; + + vrend_renderer_fill_caps_v2(client, gles_ver, caps); +} + +void vrend_renderer_force_ctx_0(struct virgl_client *client) +{ + struct vrend_context *ctx0 = vrend_lookup_renderer_ctx(client, 0); + client->vrend_state->current_ctx = NULL; + client->vrend_state->current_hw_ctx = NULL; + vrend_hw_switch_context(ctx0, true); +} + +void vrend_renderer_attach_res_ctx(struct virgl_client *client, int ctx_id, int resource_id) +{ + struct vrend_context *ctx = vrend_lookup_renderer_ctx(client, ctx_id); + struct vrend_resource *res; + + if (!ctx) + return; + + res = vrend_resource_lookup(client, resource_id, 0); + if (!res) + return; + + vrend_object_insert_nofree(ctx->res_hash, res, sizeof(*res), resource_id, 1, false); +} + +static void vrend_renderer_detach_res_ctx(struct vrend_context *ctx, int res_handle) +{ + struct vrend_resource *res; + res = vrend_object_lookup(ctx->res_hash, res_handle, 1); + if (!res) + return; + + vrend_object_remove(ctx->res_hash, res_handle, 1); +} + +struct vrend_resource *vrend_renderer_ctx_res_lookup(struct vrend_context *ctx, int res_handle) +{ + struct vrend_resource *res = vrend_object_lookup(ctx->res_hash, res_handle, 1); + + return res; +} + +void vrend_renderer_get_cap_set(uint32_t cap_set, uint32_t *max_ver, + uint32_t *max_size) +{ + switch (cap_set) { + case VREND_CAP_SET: + *max_ver = 1; + *max_size = sizeof(struct virgl_caps_v1); + break; + case VREND_CAP_SET2: + /* we should never need to increase this - it should be possible to just grow virgl_caps */ + *max_ver = 2; + *max_size = sizeof(struct virgl_caps_v2); + break; + default: + *max_ver = 0; + *max_size = 0; + break; + } +} + +void vrend_renderer_create_sub_ctx(struct vrend_context *ctx, int sub_ctx_id) +{ + struct vrend_sub_context *sub; + GLuint i; + + LIST_FOR_EACH_ENTRY(sub, &ctx->sub_ctxs, head) { + if (sub->sub_ctx_id == sub_ctx_id) { + return; + } + } + + sub = CALLOC_STRUCT(vrend_sub_context); + if (!sub) + return; + + sub->gl_context = vrend_clicbs->create_gl_context(ctx->client); + vrend_clicbs->make_current(ctx->client, sub->gl_context); + + sub->sub_ctx_id = sub_ctx_id; + + /* initialize the depth far_val to 1 */ + for (i = 0; i < PIPE_MAX_VIEWPORTS; i++) { + sub->vps[i].far_val = 1.0; + } + + if (!has_feature(feat_gles31_vertex_attrib_binding)) { + glGenVertexArrays(1, &sub->vaoid); + glBindVertexArray(sub->vaoid); + } + + glGenFramebuffers(1, &sub->fb_id); + glGenFramebuffers(2, sub->blit_fb_ids); + + list_inithead(&sub->programs); + list_inithead(&sub->streamout_list); + + sub->object_hash = vrend_object_init_ctx_table(); + + ctx->sub = sub; + list_add(&sub->head, &ctx->sub_ctxs); + if (sub_ctx_id == 0) + ctx->sub0 = sub; +} + +void vrend_renderer_destroy_sub_ctx(struct vrend_context *ctx, int sub_ctx_id) +{ + struct vrend_sub_context *sub, *tofree = NULL; + + /* never destroy sub context id 0 */ + if (sub_ctx_id == 0) + return; + + LIST_FOR_EACH_ENTRY(sub, &ctx->sub_ctxs, head) { + if (sub->sub_ctx_id == sub_ctx_id) { + tofree = sub; + } + } + + if (tofree) { + if (ctx->sub == tofree) { + ctx->sub = ctx->sub0; + vrend_clicbs->make_current(ctx->client, ctx->sub->gl_context); + } + vrend_destroy_sub_context(ctx->client, tofree); + } +} + +void vrend_renderer_set_sub_ctx(struct vrend_context *ctx, int sub_ctx_id) +{ + struct vrend_sub_context *sub; + /* find the sub ctx */ + + if (ctx->sub && ctx->sub->sub_ctx_id == sub_ctx_id) + return; + + LIST_FOR_EACH_ENTRY(sub, &ctx->sub_ctxs, head) { + if (sub->sub_ctx_id == sub_ctx_id) { + ctx->sub = sub; + vrend_clicbs->make_current(ctx->client, sub->gl_context); + break; + } + } +} \ No newline at end of file diff --git a/app/src/main/cpp/virglrenderer/src/vrend_renderer.h b/app/src/main/cpp/virglrenderer/src/vrend_renderer.h new file mode 100644 index 0000000..74f0996 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_renderer.h @@ -0,0 +1,405 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef VREND_RENDERER_H +#define VREND_RENDERER_H + +#include "pipe/p_state.h" +#include "util/u_inlines.h" +#include "util/u_double_list.h" +#include "os/os_thread.h" + +#include "virgl_protocol.h" +#include "vrend_iov.h" +#include "virgl_hw.h" +#include "vrend_util.h" + +typedef void *virgl_gl_context; + +struct vrend_context; +struct virgl_client; + +/* Number of mipmap levels for which to keep the backing iov offsets. + * Value mirrored from mesa/virgl + */ +#define VR_MAX_TEXTURE_2D_LEVELS 15 + +#define VREND_MAX_CTX 64 + +#define VREND_STORAGE_GUEST_MEMORY BIT(0) +#define VREND_STORAGE_GL_TEXTURE BIT(1) +#define VREND_STORAGE_GL_BUFFER BIT(2) +#define VREND_STORAGE_HOST_SYSTEM_MEMORY BIT(3) +#define VREND_STORAGE_GL_IMMUTABLE BIT(4) + +struct vrend_resource { + struct pipe_resource base; + uint32_t storage_bits; + + GLuint id; + GLenum target; + + /* fb id if we need to readback this resource */ + GLuint readback_fb_id; + GLuint readback_fb_level; + GLuint readback_fb_z; + + GLuint tbo_tex_id;/* tbos have two ids to track */ + bool y_0_top; + + GLuint handle; + + void *priv; + /* Pointer to system memory storage for this resource. Only valid for + * VREND_RESOURCE_STORAGE_GUEST_ELSE_SYSTEM buffer storage. + */ + char *ptr; + /* IOV pointing to shared guest memory storage for this resource. */ + struct iovec *iov; + uint32_t num_iovs; + uint64_t mipmap_offsets[VR_MAX_TEXTURE_2D_LEVELS]; +}; + +#define VIRGL_TEXTURE_NEED_SWIZZLE (1 << 0) +#define VIRGL_TEXTURE_CAN_TEXTURE_STORAGE (1 << 1) +#define VIRGL_TEXTURE_CAN_READBACK (1 << 2) + +struct vrend_format_table { + enum virgl_formats format; + GLenum internalformat; + GLenum glformat; + GLenum gltype; + uint8_t swizzle[4]; + uint32_t bindings; + uint32_t flags; +}; + +struct vrend_if_cbs { + void (*write_fence)(struct virgl_client *client, unsigned fence_id); + virgl_gl_context (*create_gl_context)(struct virgl_client *client); + void (*destroy_gl_context)(struct virgl_client *client, virgl_gl_context ctx); + int (*make_current)(struct virgl_client *client, virgl_gl_context ctx); +}; + +struct vrend_state { + struct vrend_context *current_ctx; + struct vrend_context *current_hw_ctx; + struct list_head waiting_query_list; + + /* these appeared broken on at least one driver */ + bool use_explicit_locations; + uint32_t max_draw_buffers; + uint32_t max_texture_2d_size; + uint32_t max_texture_3d_size; + uint32_t max_texture_cube_size; + struct list_head active_ctx_list; + + struct list_head fence_list; + struct list_head fence_wait_list; + + /* Needed on GLES to inject a TCS */ + float tess_factors[6]; +}; + +int vrend_renderer_init(struct virgl_client *client, struct vrend_if_cbs *cbs); + +void vrend_insert_format(struct vrend_format_table *entry, uint32_t bindings, uint32_t flags); +bool vrend_check_framebuffer_mixed_color_attachements(void); + +void vrend_insert_format_swizzle(int override_format, struct vrend_format_table *entry, + uint32_t bindings, uint8_t swizzle[4], uint32_t flags); +const struct vrend_format_table *vrend_get_format_table_entry(enum virgl_formats format); + +int vrend_create_shader(struct vrend_context *ctx, + uint32_t handle, + const struct pipe_stream_output_info *stream_output, + uint32_t req_local_mem, + const char *shd_text, uint32_t offlen, uint32_t num_tokens, + uint32_t type, uint32_t pkt_length); + +void vrend_bind_shader(struct vrend_context *ctx, + uint32_t type, + uint32_t handle); + +void vrend_clear(struct vrend_context *ctx, + unsigned buffers, + const union pipe_color_union *color, + double depth, unsigned stencil); + +int vrend_draw_vbo(struct vrend_context *ctx, + const struct pipe_draw_info *info, + uint32_t cso, uint32_t indirect_handle, uint32_t indirect_draw_count_handle); + +void vrend_set_framebuffer_state(struct vrend_context *ctx, + uint32_t nr_cbufs, uint32_t surf_handle[PIPE_MAX_COLOR_BUFS], + uint32_t zsurf_handle); + +struct vrend_context *vrend_create_context(struct virgl_client *client, int id); +bool vrend_destroy_context(struct vrend_context *ctx); +int vrend_renderer_context_create(struct virgl_client *client, uint32_t handle); +void vrend_renderer_context_create_internal(struct virgl_client *client, uint32_t handle); +void vrend_renderer_context_destroy(struct virgl_client *client, uint32_t handle); + +struct vrend_renderer_resource_create_args { + uint32_t handle; + enum pipe_texture_target target; + uint32_t format; + uint32_t bind; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t array_size; + uint32_t last_level; + uint32_t nr_samples; + uint32_t flags; +}; + +int vrend_renderer_resource_create(struct virgl_client *client, struct vrend_renderer_resource_create_args *args, struct iovec *iov, uint32_t num_iovs); +void vrend_renderer_resource_unref(struct virgl_client *client, uint32_t handle); + +int vrend_create_surface(struct vrend_context *ctx, + uint32_t handle, + uint32_t res_handle, uint32_t format, + uint32_t val0, uint32_t val1); +int vrend_create_sampler_view(struct vrend_context *ctx, + uint32_t handle, + uint32_t res_handle, uint32_t format, + uint32_t val0, uint32_t val1, uint32_t swizzle_packed); + +int vrend_create_sampler_state(struct vrend_context *ctx, + uint32_t handle, + struct pipe_sampler_state *templ); + +int vrend_create_so_target(struct vrend_context *ctx, + uint32_t handle, + uint32_t res_handle, + uint32_t buffer_offset, + uint32_t buffer_size); + +void vrend_set_streamout_targets(struct vrend_context *ctx, + uint32_t append_bitmask, + uint32_t num_targets, + uint32_t *handles); + +int vrend_create_vertex_elements_state(struct vrend_context *ctx, + uint32_t handle, + unsigned num_elements, + const struct pipe_vertex_element *elements); +void vrend_bind_vertex_elements_state(struct vrend_context *ctx, + uint32_t handle); + +void vrend_set_single_vbo(struct vrend_context *ctx, + uint32_t index, + uint32_t stride, + uint32_t buffer_offset, + uint32_t res_handle); +void vrend_set_num_vbo(struct vrend_context *ctx, + int num_vbo); + +int vrend_transfer_inline_write(struct vrend_context *ctx, + struct vrend_transfer_info *info); + +int vrend_renderer_copy_transfer3d(struct vrend_context *ctx, + struct vrend_transfer_info *info, + uint32_t src_handle); + +void vrend_set_viewport_states(struct vrend_context *ctx, + uint32_t start_slot, uint32_t num_viewports, + const struct pipe_viewport_state *state); +void vrend_set_num_sampler_views(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t start_slot, + uint32_t num_sampler_views); +void vrend_set_single_sampler_view(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t index, + uint32_t res_handle); + +void vrend_object_bind_blend(struct vrend_context *ctx, + uint32_t handle); +void vrend_object_bind_dsa(struct vrend_context *ctx, + uint32_t handle); +void vrend_object_bind_rasterizer(struct vrend_context *ctx, + uint32_t handle); + +void vrend_bind_sampler_states(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t start_slot, + uint32_t num_states, + uint32_t *handles); +void vrend_set_index_buffer(struct vrend_context *ctx, + uint32_t res_handle, + uint32_t index_size, + uint32_t offset); +void vrend_set_single_image_view(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t index, + uint32_t format, uint32_t access, + uint32_t layer_offset, uint32_t level_size, + uint32_t handle); +void vrend_set_single_ssbo(struct vrend_context *ctx, + uint32_t shader_type, + uint32_t index, + uint32_t offset, uint32_t length, + uint32_t handle); +void vrend_set_single_abo(struct vrend_context *ctx, + uint32_t index, + uint32_t offset, uint32_t length, + uint32_t handle); +void vrend_memory_barrier(struct vrend_context *ctx, + unsigned flags); +void vrend_launch_grid(struct vrend_context *ctx, + uint32_t *block, + uint32_t *grid, + uint32_t indirect_handle, + uint32_t indirect_offset); +void vrend_set_framebuffer_state_no_attach(struct vrend_context *ctx, + uint32_t width, uint32_t height, + uint32_t layers, uint32_t samples); + +int vrend_renderer_transfer_iov(struct virgl_client *client, const struct vrend_transfer_info *info, int transfer_mode); + +void vrend_renderer_resource_copy_region(struct vrend_context *ctx, + uint32_t dst_handle, uint32_t dst_level, + uint32_t dstx, uint32_t dsty, uint32_t dstz, + uint32_t src_handle, uint32_t src_level, + const struct pipe_box *src_box); + +void vrend_renderer_blit(struct vrend_context *ctx, + uint32_t dst_handle, uint32_t src_handle, + const struct pipe_blit_info *info); + +void vrend_set_stencil_ref(struct vrend_context *ctx, struct pipe_stencil_ref *ref); +void vrend_set_blend_color(struct vrend_context *ctx, struct pipe_blend_color *color); +void vrend_set_scissor_state(struct vrend_context *ctx, + uint32_t start_slot, + uint32_t num_scissor, + struct pipe_scissor_state *ss); + +void vrend_set_polygon_stipple(struct vrend_context *ctx, struct pipe_poly_stipple *ps); + +void vrend_set_clip_state(struct vrend_context *ctx, struct pipe_clip_state *ucp); +void vrend_set_sample_mask(struct vrend_context *ctx, unsigned sample_mask); +void vrend_set_min_samples(struct vrend_context *ctx, unsigned min_samples); + +void vrend_set_constants(struct vrend_context *ctx, + uint32_t shader, + uint32_t index, + uint32_t num_constant, + float *data); + +void vrend_set_uniform_buffer(struct vrend_context *ctx, uint32_t shader, + uint32_t index, uint32_t offset, uint32_t length, + uint32_t res_handle); + +void vrend_fb_bind_texture_id(struct vrend_resource *res, + int id, + int idx, + uint32_t level, uint32_t layer); + +void vrend_set_tess_state(struct vrend_context *ctx, const float tess_factors[6]); + +void vrend_renderer_fini(struct virgl_client *client); + +int vrend_decode_block(struct virgl_client *client, uint32_t ctx_id, uint32_t *block, int ndw); +struct vrend_context *vrend_lookup_renderer_ctx(struct virgl_client *client, uint32_t ctx_id); + +int vrend_renderer_create_fence(struct virgl_client *client, int client_fence_id, uint32_t ctx_id); + +void vrend_renderer_check_fences(struct virgl_client *client); + +bool vrend_hw_switch_context(struct vrend_context *ctx, bool now); +uint32_t vrend_renderer_object_insert(struct vrend_context *ctx, void *data, + uint32_t size, uint32_t handle, enum virgl_object_type type); +void vrend_renderer_object_destroy(struct vrend_context *ctx, uint32_t handle); + +int vrend_create_query(struct vrend_context *ctx, uint32_t handle, + uint32_t query_type, uint32_t query_index, + uint32_t res_handle, uint32_t offset); + +int vrend_begin_query(struct vrend_context *ctx, uint32_t handle); +int vrend_end_query(struct vrend_context *ctx, uint32_t handle); +void vrend_get_query_result(struct vrend_context *ctx, uint32_t handle, + uint32_t wait); + +void vrend_renderer_fill_caps(struct virgl_client *client, uint32_t set, uint32_t version, union virgl_caps *caps); + +void vrend_build_format_list(void); +void vrend_check_texture_storage(struct vrend_format_table *table); + +int vrend_renderer_resource_attach_iov(struct virgl_client *client, int res_handle, struct iovec *iov, int num_iovs); +void vrend_renderer_resource_detach_iov(struct virgl_client *client, int res_handle, struct iovec **iov_p, int *num_iovs_p); +void vrend_renderer_resource_destroy(struct vrend_resource *res); + +static inline void +vrend_resource_reference(struct vrend_resource **ptr, struct vrend_resource *tex) +{ + struct vrend_resource *old_tex = *ptr; + + if (pipe_reference(&(*ptr)->base.reference, &tex->base.reference)) + vrend_renderer_resource_destroy(old_tex); + *ptr = tex; +} + +void vrend_renderer_force_ctx_0(struct virgl_client *client); + +void vrend_renderer_attach_res_ctx(struct virgl_client *client, int ctx_id, int resource_id); + +struct vrend_resource *vrend_renderer_ctx_res_lookup(struct vrend_context *ctx, int res_handle); + +#define VREND_CAP_SET 1 +#define VREND_CAP_SET2 2 + +void vrend_renderer_get_cap_set(uint32_t cap_set, uint32_t *max_ver, + uint32_t *max_size); + +void vrend_renderer_create_sub_ctx(struct vrend_context *ctx, int sub_ctx_id); +void vrend_renderer_destroy_sub_ctx(struct vrend_context *ctx, int sub_ctx_id); +void vrend_renderer_set_sub_ctx(struct vrend_context *ctx, int sub_ctx_id); + +void vrend_fb_bind_texture(struct vrend_resource *res, + int idx, + uint32_t level, uint32_t layer); +boolean format_is_copy_compatible(enum virgl_formats src, enum virgl_formats dst, + boolean allow_compressed); + +/* blitter interface */ +void vrend_renderer_blit_gl(struct virgl_client *client, + struct vrend_resource *src_res, + struct vrend_resource *dst_res, + GLenum blit_views[2], + const struct pipe_blit_info *info, + bool has_texture_srgb_decode, + bool has_srgb_write_control, + bool skip_dest_swizzle); +void vrend_blitter_fini(struct virgl_client *client); + +void vrend_decode_reset(struct virgl_client *client, bool ctx_0_only); + +unsigned vrend_renderer_query_multisample_caps(unsigned max_samples, + struct virgl_caps_v2 *caps); + +extern struct vrend_if_cbs *vrend_clicbs; + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/vrend_shader.c b/app/src/main/cpp/virglrenderer/src/vrend_shader.c new file mode 100644 index 0000000..69edddd --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_shader.c @@ -0,0 +1,6749 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "tgsi/tgsi_info.h" +#include "tgsi/tgsi_iterate.h" +#include "tgsi/tgsi_scan.h" +#include "util/u_memory.h" +#include "util/u_math.h" +#include +#include +#include +#include +#include "vrend_shader.h" + +#include "vrend_strbuf.h" +#include "vrend_util.h" + +/* start convert of tgsi to glsl */ + +#define INTERP_PREFIX " " +#define INVARI_PREFIX "invariant" + +#define SHADER_REQ_NONE 0 +#define SHADER_REQ_SAMPLER_RECT (1 << 0) +#define SHADER_REQ_CUBE_ARRAY (1 << 1) +#define SHADER_REQ_INTS (1 << 2) +#define SHADER_REQ_SAMPLER_MS (1 << 3) +#define SHADER_REQ_INSTANCE_ID (1 << 4) +#define SHADER_REQ_LODQ (1 << 5) +#define SHADER_REQ_TXQ_LEVELS (1 << 6) +#define SHADER_REQ_TG4 (1 << 7) +#define SHADER_REQ_VIEWPORT_IDX (1 << 8) +#define SHADER_REQ_STENCIL_EXPORT (1 << 9) +#define SHADER_REQ_LAYER (1 << 10) +#define SHADER_REQ_SAMPLE_SHADING (1 << 11) +#define SHADER_REQ_GPU_SHADER5 (1 << 12) +#define SHADER_REQ_DERIVATIVE_CONTROL (1 << 13) +#define SHADER_REQ_FP64 (1 << 14) +#define SHADER_REQ_IMAGE_LOAD_STORE (1 << 15) +#define SHADER_REQ_ES31_COMPAT (1 << 16) +#define SHADER_REQ_IMAGE_SIZE (1 << 17) +#define SHADER_REQ_TXQS (1 << 18) +#define SHADER_REQ_FBFETCH (1 << 19) +#define SHADER_REQ_SHADER_CLOCK (1 << 20) +#define SHADER_REQ_PSIZE (1 << 21) +#define SHADER_REQ_IMAGE_ATOMIC (1 << 22) +#define SHADER_REQ_CLIP_DISTANCE (1 << 23) +#define SHADER_REQ_ENHANCED_LAYOUTS (1 << 24) +#define SHADER_REQ_SEPERATE_SHADER_OBJECTS (1 << 25) +#define SHADER_REQ_ARRAYS_OF_ARRAYS (1 << 26) +#define SHADER_REQ_SHADER_INTEGER_FUNC (1 << 27) +#define SHADER_REQ_SHADER_ATOMIC_FLOAT (1 << 28) +#define SHADER_REQ_NV_IMAGE_FORMATS (1 << 29) +#define SHADER_REQ_CONSERVATIVE_DEPTH (1 << 30) +#define SHADER_REQ_SAMPLER_BUF (1 << 31) + +#define FRONT_COLOR_EMITTED (1 << 0) +#define BACK_COLOR_EMITTED (1 << 1); + +struct vrend_shader_io { + unsigned name; + unsigned done; + int sid; + unsigned interpolate; + int first; + int last; + int array_id; + uint8_t usage_mask; + int swizzle_offset; + int num_components; + int layout_location; + unsigned location; + bool invariant; + bool precise; + bool glsl_predefined_no_emit; + bool glsl_no_index; + bool glsl_gl_block; + bool override_no_wm; + bool is_int; + bool fbfetch_used; + char glsl_name[128]; + unsigned stream; +}; + +struct vrend_shader_sampler { + int tgsi_sampler_type; + enum tgsi_return_type tgsi_sampler_return; +}; + +struct vrend_shader_table { + uint32_t key; + const char *string; +}; + +struct vrend_shader_image { + struct tgsi_declaration_image decl; + enum tgsi_return_type image_return; + bool vflag; +}; + +#define MAX_IMMEDIATE 1024 +struct immed { + int type; + union imm { + uint32_t ui; + int32_t i; + float f; + } val[4]; +}; + +struct vrend_temp_range { + int first; + int last; + int array_id; +}; + +struct vrend_io_range { + struct vrend_shader_io io; + bool used; +}; + +struct dump_ctx { + struct tgsi_iterate_context iter; + struct vrend_shader_cfg *cfg; + struct tgsi_shader_info info; + int prog_type; + int size; + struct vrend_strbuf glsl_main; + int indent_level; + struct vrend_strbuf glsl_hdr; + struct vrend_strbuf glsl_ver_ext; + uint instno; + + struct vrend_strbuf src_bufs[4]; + + uint32_t num_interps; + uint32_t num_inputs; + uint32_t attrib_input_mask; + struct vrend_shader_io inputs[64]; + uint32_t num_outputs; + struct vrend_shader_io outputs[64]; + uint8_t front_back_color_emitted_flags[64]; + uint32_t num_system_values; + struct vrend_shader_io system_values[32]; + + bool guest_sent_io_arrays; + struct vrend_io_range generic_input_range; + struct vrend_io_range patch_input_range; + struct vrend_io_range generic_output_range; + struct vrend_io_range patch_output_range; + + uint32_t generic_outputs_expected_mask; + uint32_t generic_inputs_emitted_mask; + uint32_t generic_outputs_emitted_mask; + + uint32_t num_temp_ranges; + struct vrend_temp_range *temp_ranges; + + struct vrend_shader_sampler samplers[32]; + uint32_t samplers_used; + + uint32_t ssbo_used_mask; + uint32_t ssbo_atomic_mask; + uint32_t ssbo_array_base; + uint32_t ssbo_atomic_array_base; + uint32_t ssbo_integer_mask; + uint8_t ssbo_memory_qualifier[32]; + + struct vrend_shader_image images[32]; + uint32_t images_used_mask; + + struct vrend_array *image_arrays; + uint32_t num_image_arrays; + + struct vrend_array *sampler_arrays; + uint32_t num_sampler_arrays; + + int num_consts; + int num_imm; + struct immed imm[MAX_IMMEDIATE]; + + uint32_t req_local_mem; + bool integer_memory; + + uint32_t ubo_base; + uint32_t ubo_used_mask; + int ubo_sizes[32]; + uint32_t num_address; + + uint32_t num_abo; + int abo_idx[32]; + int abo_sizes[32]; + int abo_offsets[32]; + + uint32_t shader_req_bits; + + struct pipe_stream_output_info *so; + char **so_names; + bool write_so_outputs[PIPE_MAX_SO_OUTPUTS]; + bool write_all_cbufs; + uint32_t shadow_samp_mask; + + int fs_coord_origin, fs_pixel_center; + int fs_depth_layout; + + int gs_in_prim, gs_out_prim, gs_max_out_verts; + int gs_num_invocations; + + struct vrend_shader_key *key; + int num_in_clip_dist; + int num_clip_dist; + int fs_uses_clipdist_input; + int glsl_ver_required; + int color_in_mask; + /* only used when cull is enabled */ + uint8_t num_cull_dist_prop, num_clip_dist_prop; + bool front_face_emitted; + + bool has_clipvertex; + bool has_clipvertex_so; + bool vs_has_pervertex; + bool write_mul_utemp; + bool write_mul_itemp; + bool has_sample_input; + bool early_depth_stencil; + bool has_file_memory; + bool force_color_two_side; + bool winsys_adjust_y_emitted; + + int tcs_vertices_out; + int tes_prim_mode; + int tes_spacing; + int tes_vertex_order; + int tes_point_mode; + + uint16_t local_cs_block_size[3]; +}; + +enum vrend_type_qualifier { + TYPE_CONVERSION_NONE = 0, + FLOAT = 1, + VEC2 = 2, + VEC3 = 3, + VEC4 = 4, + INT = 5, + IVEC2 = 6, + IVEC3 = 7, + IVEC4 = 8, + UINT = 9, + UVEC2 = 10, + UVEC3 = 11, + UVEC4 = 12, + FLOAT_BITS_TO_UINT = 13, + UINT_BITS_TO_FLOAT = 14, + FLOAT_BITS_TO_INT = 15, + INT_BITS_TO_FLOAT = 16, + DOUBLE = 17, + DVEC2 = 18, +}; + +struct dest_info { + enum vrend_type_qualifier dtypeprefix; + enum vrend_type_qualifier dstconv; + enum vrend_type_qualifier udstconv; + enum vrend_type_qualifier idstconv; + bool dst_override_no_wm[2]; +}; + +struct source_info { + enum vrend_type_qualifier svec4; + uint32_t sreg_index; + bool tg4_has_component; + bool override_no_wm[3]; + bool override_no_cast[3]; + int imm_value; +}; + +static const struct vrend_shader_table conversion_table[] = +{ + {TYPE_CONVERSION_NONE, ""}, + {FLOAT, "float"}, + {VEC2, "vec2"}, + {VEC3, "vec3"}, + {VEC4, "vec4"}, + {INT, "int"}, + {IVEC2, "ivec2"}, + {IVEC3, "ivec3"}, + {IVEC4, "ivec4"}, + {UINT, "uint"}, + {UVEC2, "uvec2"}, + {UVEC3, "uvec3"}, + {UVEC4, "uvec4"}, + {FLOAT_BITS_TO_UINT, "floatBitsToUint"}, + {UINT_BITS_TO_FLOAT, "uintBitsToFloat"}, + {FLOAT_BITS_TO_INT, "floatBitsToInt"}, + {INT_BITS_TO_FLOAT, "intBitsToFloat"}, + {DOUBLE, "double"}, + {DVEC2, "dvec2"}, +}; + +enum io_type { + io_in, + io_out +}; + +/* We prefer arrays of arrays, but if this is not available then TCS, GEOM, and TES + * inputs must be blocks, but FS input should not because interpolateAt* doesn't + * support dereferencing block members. */ +static inline bool prefer_generic_io_block(struct dump_ctx *ctx, enum io_type io) +{ + switch (ctx->prog_type) { + case TGSI_PROCESSOR_FRAGMENT: + return false; + + case TGSI_PROCESSOR_TESS_CTRL: + return true; + + case TGSI_PROCESSOR_TESS_EVAL: + return io == io_in ? true : (ctx->key->gs_present ? true : false); + + case TGSI_PROCESSOR_GEOMETRY: + return io == io_in; + + case TGSI_PROCESSOR_VERTEX: + if (io == io_in) + return false; + return (ctx->key->gs_present || ctx->key->tes_present); + + default: + return false; + } +} + +static inline const char *get_string(enum vrend_type_qualifier key) +{ + if (key >= ARRAY_SIZE(conversion_table)) + return conversion_table[TYPE_CONVERSION_NONE].string; + + return conversion_table[key].string; +} + +static inline const char *get_wm_string(unsigned wm) +{ + switch(wm) { + case TGSI_WRITEMASK_NONE: + return ""; + case TGSI_WRITEMASK_X: + return ".x"; + case TGSI_WRITEMASK_XY: + return ".xy"; + case TGSI_WRITEMASK_XYZ: + return ".xyz"; + case TGSI_WRITEMASK_W: + return ".w"; + default: + return ""; + } +} + +const char *get_internalformat_string(int virgl_format, enum tgsi_return_type *stype); + +static inline const char *tgsi_proc_to_prefix(int shader_type) +{ + switch (shader_type) { + case TGSI_PROCESSOR_VERTEX: return "vs"; + case TGSI_PROCESSOR_FRAGMENT: return "fs"; + case TGSI_PROCESSOR_GEOMETRY: return "gs"; + case TGSI_PROCESSOR_TESS_CTRL: return "tc"; + case TGSI_PROCESSOR_TESS_EVAL: return "te"; + case TGSI_PROCESSOR_COMPUTE: return "cs"; + default: + return NULL; + }; +} + +static inline const char *prim_to_name(int prim) +{ + switch (prim) { + case PIPE_PRIM_POINTS: return "points"; + case PIPE_PRIM_LINES: return "lines"; + case PIPE_PRIM_LINE_STRIP: return "line_strip"; + case PIPE_PRIM_LINES_ADJACENCY: return "lines_adjacency"; + case PIPE_PRIM_TRIANGLES: return "triangles"; + case PIPE_PRIM_TRIANGLE_STRIP: return "triangle_strip"; + case PIPE_PRIM_TRIANGLES_ADJACENCY: return "triangles_adjacency"; + case PIPE_PRIM_QUADS: return "quads"; + default: return "UNKNOWN"; + }; +} + +static inline const char *prim_to_tes_name(int prim) +{ + switch (prim) { + case PIPE_PRIM_QUADS: return "quads"; + case PIPE_PRIM_TRIANGLES: return "triangles"; + case PIPE_PRIM_LINES: return "isolines"; + default: return "UNKNOWN"; + } +} + +static const char *get_spacing_string(int spacing) +{ + switch (spacing) { + case PIPE_TESS_SPACING_FRACTIONAL_ODD: + return "fractional_odd_spacing"; + case PIPE_TESS_SPACING_FRACTIONAL_EVEN: + return "fractional_even_spacing"; + case PIPE_TESS_SPACING_EQUAL: + default: + return "equal_spacing"; + } +} + +static inline int gs_input_prim_to_size(int prim) +{ + switch (prim) { + case PIPE_PRIM_POINTS: return 1; + case PIPE_PRIM_LINES: return 2; + case PIPE_PRIM_LINES_ADJACENCY: return 4; + case PIPE_PRIM_TRIANGLES: return 3; + case PIPE_PRIM_TRIANGLES_ADJACENCY: return 6; + default: return -1; + }; +} + +static const char *get_stage_input_name_prefix(struct dump_ctx *ctx, int processor) +{ + const char *name_prefix; + switch (processor) { + case TGSI_PROCESSOR_FRAGMENT: + if (ctx->key->gs_present) + name_prefix = "gso"; + else if (ctx->key->tes_present) + name_prefix = "teo"; + else + name_prefix = "vso"; + break; + case TGSI_PROCESSOR_GEOMETRY: + if (ctx->key->tes_present) + name_prefix = "teo"; + else + name_prefix = "vso"; + break; + case TGSI_PROCESSOR_TESS_EVAL: + if (ctx->key->tcs_present) + name_prefix = "tco"; + else + name_prefix = "vso"; + break; + case TGSI_PROCESSOR_TESS_CTRL: + name_prefix = "vso"; + break; + case TGSI_PROCESSOR_VERTEX: + default: + name_prefix = "in"; + break; + } + return name_prefix; +} + +static const char *get_stage_output_name_prefix(int processor) +{ + const char *name_prefix; + switch (processor) { + case TGSI_PROCESSOR_FRAGMENT: + name_prefix = "fsout"; + break; + case TGSI_PROCESSOR_GEOMETRY: + name_prefix = "gso"; + break; + case TGSI_PROCESSOR_VERTEX: + name_prefix = "vso"; + break; + case TGSI_PROCESSOR_TESS_CTRL: + name_prefix = "tco"; + break; + case TGSI_PROCESSOR_TESS_EVAL: + name_prefix = "teo"; + break; + default: + name_prefix = "out"; + break; + } + return name_prefix; +} + +static void require_glsl_ver(struct dump_ctx *ctx, int glsl_ver) +{ + if (glsl_ver > ctx->glsl_ver_required) + ctx->glsl_ver_required = glsl_ver; +} + +static void emit_indent(struct dump_ctx *ctx) +{ + if (ctx->indent_level > 0) { + /* very high levels of indentation doesn't improve readability */ + int indent_level = MIN2(ctx->indent_level, 15); + char buf[16]; + memset(buf, '\t', indent_level); + buf[indent_level] = '\0'; + strbuf_append(&ctx->glsl_main, buf); + } +} + +static void emit_buf(struct dump_ctx *ctx, const char *buf) +{ + emit_indent(ctx); + strbuf_append(&ctx->glsl_main, buf); +} + +static void indent_buf(struct dump_ctx *ctx) +{ + ctx->indent_level++; +} + +static void outdent_buf(struct dump_ctx *ctx) +{ + if (ctx->indent_level <= 0) { + strbuf_set_error(&ctx->glsl_main); + return; + } + ctx->indent_level--; +} + +static void set_buf_error(struct dump_ctx *ctx) +{ + strbuf_set_error(&ctx->glsl_main); +} + +__attribute__((format(printf, 2, 3))) +static void emit_buff(struct dump_ctx *ctx, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + emit_indent(ctx); + strbuf_vappendf(&ctx->glsl_main, fmt, va); + va_end(va); +} + +static void emit_hdr(struct dump_ctx *ctx, const char *buf) +{ + strbuf_append(&ctx->glsl_hdr, buf); +} + +static void set_hdr_error(struct dump_ctx *ctx) +{ + strbuf_set_error(&ctx->glsl_hdr); +} + +__attribute__((format(printf, 2, 3))) +static void emit_hdrf(struct dump_ctx *ctx, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + strbuf_vappendf(&ctx->glsl_hdr, fmt, va); + va_end(va); +} + +__attribute__((format(printf, 2, 3))) +static void emit_ver_extf(struct dump_ctx *ctx, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + strbuf_vappendf(&ctx->glsl_ver_ext, fmt, va); + va_end(va); +} + +static bool allocate_temp_range(struct dump_ctx *ctx, int first, int last, + int array_id) +{ + int idx = ctx->num_temp_ranges; + + ctx->temp_ranges = realloc(ctx->temp_ranges, sizeof(struct vrend_temp_range) * (idx + 1)); + if (!ctx->temp_ranges) + return false; + + ctx->temp_ranges[idx].first = first; + ctx->temp_ranges[idx].last = last; + ctx->temp_ranges[idx].array_id = array_id; + ctx->num_temp_ranges++; + return true; +} + +static struct vrend_temp_range *find_temp_range(struct dump_ctx *ctx, int index) +{ + uint32_t i; + for (i = 0; i < ctx->num_temp_ranges; i++) { + if (index >= ctx->temp_ranges[i].first && + index <= ctx->temp_ranges[i].last) + return &ctx->temp_ranges[i]; + } + return NULL; +} + +static bool samplertype_is_shadow(int sampler_type) +{ + switch (sampler_type) { + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + return true; + default: + return false; + } +} + +static uint32_t samplertype_to_req_bits(int sampler_type) +{ + + switch (sampler_type) { + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + return SHADER_REQ_CUBE_ARRAY; + case TGSI_TEXTURE_2D_MSAA: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + return SHADER_REQ_SAMPLER_MS; + case TGSI_TEXTURE_BUFFER: + return SHADER_REQ_SAMPLER_BUF; + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_RECT: + return SHADER_REQ_SAMPLER_RECT; + default: + return 0; + } +} + +static bool add_images(struct dump_ctx *ctx, int first, int last, + struct tgsi_declaration_image *img_decl) +{ + int i; + + const struct util_format_description *descr = util_format_description(img_decl->Format); + if (descr->nr_channels == 2 && + descr->swizzle[0] == UTIL_FORMAT_SWIZZLE_X && + descr->swizzle[1] == UTIL_FORMAT_SWIZZLE_Y && + descr->swizzle[2] == UTIL_FORMAT_SWIZZLE_0 && + descr->swizzle[3] == UTIL_FORMAT_SWIZZLE_1) { + ctx->shader_req_bits |= SHADER_REQ_NV_IMAGE_FORMATS; + } else if (img_decl->Format == PIPE_FORMAT_R11G11B10_FLOAT || + img_decl->Format == PIPE_FORMAT_R10G10B10A2_UINT || + img_decl->Format == PIPE_FORMAT_R10G10B10A2_UNORM || + img_decl->Format == PIPE_FORMAT_R16G16B16A16_UNORM|| + img_decl->Format == PIPE_FORMAT_R16G16B16A16_SNORM) + ctx->shader_req_bits |= SHADER_REQ_NV_IMAGE_FORMATS; + else if (descr->nr_channels == 1 && + descr->swizzle[0] == UTIL_FORMAT_SWIZZLE_X && + descr->swizzle[1] == UTIL_FORMAT_SWIZZLE_0 && + descr->swizzle[2] == UTIL_FORMAT_SWIZZLE_0 && + descr->swizzle[3] == UTIL_FORMAT_SWIZZLE_1 && + (descr->channel[0].size == 8 || descr->channel[0].size ==16)) + ctx->shader_req_bits |= SHADER_REQ_NV_IMAGE_FORMATS; + + for (i = first; i <= last; i++) { + ctx->images[i].decl = *img_decl; + ctx->images[i].vflag = false; + ctx->images_used_mask |= (1 << i); + + if (!samplertype_is_shadow(ctx->images[i].decl.Resource)) + ctx->shader_req_bits |= samplertype_to_req_bits(ctx->images[i].decl.Resource); + } + + if (ctx->info.indirect_files & (1 << TGSI_FILE_IMAGE)) { + if (ctx->num_image_arrays) { + struct vrend_array *last_array = &ctx->image_arrays[ctx->num_image_arrays - 1]; + /* + * If this set of images is consecutive to the last array, + * and has compatible return and decls, then increase the array size. + */ + if ((last_array->first + last_array->array_size == first) && + !memcmp(&ctx->images[last_array->first].decl, &ctx->images[first].decl, sizeof(ctx->images[first].decl)) && + ctx->images[last_array->first].image_return == ctx->images[first].image_return) { + last_array->array_size += last - first + 1; + return true; + } + } + + /* allocate a new image array for this range of images */ + ctx->num_image_arrays++; + ctx->image_arrays = realloc(ctx->image_arrays, sizeof(struct vrend_array) * ctx->num_image_arrays); + if (!ctx->image_arrays) + return false; + ctx->image_arrays[ctx->num_image_arrays - 1].first = first; + ctx->image_arrays[ctx->num_image_arrays - 1].array_size = last - first + 1; + } + return true; +} + +static bool add_sampler_array(struct dump_ctx *ctx, int first, int last) +{ + int idx = ctx->num_sampler_arrays; + ctx->num_sampler_arrays++; + ctx->sampler_arrays = realloc(ctx->sampler_arrays, sizeof(struct vrend_array) * ctx->num_sampler_arrays); + if (!ctx->sampler_arrays) + return false; + + ctx->sampler_arrays[idx].first = first; + ctx->sampler_arrays[idx].array_size = last - first + 1; + return true; +} + +static int lookup_sampler_array(struct dump_ctx *ctx, int index) +{ + uint32_t i; + for (i = 0; i < ctx->num_sampler_arrays; i++) { + int last = ctx->sampler_arrays[i].first + ctx->sampler_arrays[i].array_size - 1; + if (index >= ctx->sampler_arrays[i].first && + index <= last) { + return ctx->sampler_arrays[i].first; + } + } + return -1; +} + +int vrend_shader_lookup_sampler_array(struct vrend_shader_info *sinfo, int index) +{ + int i; + for (i = 0; i < sinfo->num_sampler_arrays; i++) { + int last = sinfo->sampler_arrays[i].first + sinfo->sampler_arrays[i].array_size - 1; + if (index >= sinfo->sampler_arrays[i].first && + index <= last) { + return sinfo->sampler_arrays[i].first; + } + } + return -1; +} + +static bool add_samplers(struct dump_ctx *ctx, int first, int last, int sview_type, enum tgsi_return_type sview_rtype) +{ + if (sview_rtype == TGSI_RETURN_TYPE_SINT || + sview_rtype == TGSI_RETURN_TYPE_UINT) + ctx->shader_req_bits |= SHADER_REQ_INTS; + + for (int i = first; i <= last; i++) { + ctx->samplers[i].tgsi_sampler_return = sview_rtype; + ctx->samplers[i].tgsi_sampler_type = sview_type; + } + + if (ctx->info.indirect_files & (1 << TGSI_FILE_SAMPLER)) { + if (ctx->num_sampler_arrays) { + struct vrend_array *last_array = &ctx->sampler_arrays[ctx->num_sampler_arrays - 1]; + if ((last_array->first + last_array->array_size == first) && + ctx->samplers[last_array->first].tgsi_sampler_type == sview_type && + ctx->samplers[last_array->first].tgsi_sampler_return == sview_rtype) { + last_array->array_size += last - first + 1; + return true; + } + } + + /* allocate a new image array for this range of images */ + return add_sampler_array(ctx, first, last); + } + return true; +} + +static struct vrend_array *lookup_image_array_ptr(struct dump_ctx *ctx, int index) +{ + uint32_t i; + for (i = 0; i < ctx->num_image_arrays; i++) { + if (index >= ctx->image_arrays[i].first && + index <= ctx->image_arrays[i].first + ctx->image_arrays[i].array_size - 1) { + return &ctx->image_arrays[i]; + } + } + return NULL; +} + +static int lookup_image_array(struct dump_ctx *ctx, int index) +{ + struct vrend_array *image = lookup_image_array_ptr(ctx, index); + return image ? image->first : -1; +} + +static boolean +iter_inputs(struct tgsi_iterate_context *iter, + struct tgsi_full_declaration *decl ) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + switch (decl->Declaration.File) { + case TGSI_FILE_INPUT: + for (uint32_t j = 0; j < ctx->num_inputs; j++) { + if (ctx->inputs[j].name == decl->Semantic.Name && + ctx->inputs[j].sid == decl->Semantic.Index && + ctx->inputs[j].first == decl->Range.First) + return true; + } + ctx->inputs[ctx->num_inputs].name = decl->Semantic.Name; + ctx->inputs[ctx->num_inputs].first = decl->Range.First; + ctx->inputs[ctx->num_inputs].last = decl->Range.Last; + ctx->num_inputs++; + } + return true; +} + +static bool logiop_require_inout(struct vrend_shader_key *key) +{ + if (!key->fs_logicop_enabled) + return false; + + switch (key->fs_logicop_func) { + case PIPE_LOGICOP_CLEAR: + case PIPE_LOGICOP_SET: + case PIPE_LOGICOP_COPY: + case PIPE_LOGICOP_COPY_INVERTED: + return false; + default: + return true; + } +} + +static boolean +iter_declaration(struct tgsi_iterate_context *iter, + struct tgsi_full_declaration *decl ) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + int i; + int color_offset = 0; + const char *name_prefix = ""; + bool add_two_side = false; + unsigned mask_temp; + + switch (decl->Declaration.File) { + case TGSI_FILE_INPUT: + for (uint32_t j = 0; j < ctx->num_inputs; j++) { + if (ctx->inputs[j].name == decl->Semantic.Name && + ctx->inputs[j].sid == decl->Semantic.Index && + ctx->inputs[j].first == decl->Range.First && + ctx->inputs[j].usage_mask == decl->Declaration.UsageMask && + ((!decl->Declaration.Array && ctx->inputs[j].array_id == 0) || + (ctx->inputs[j].array_id == decl->Array.ArrayID))) + return true; + } + i = ctx->num_inputs++; + if (ctx->num_inputs > ARRAY_SIZE(ctx->inputs)) + return false; + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX) { + ctx->attrib_input_mask |= (1 << decl->Range.First); + } + ctx->inputs[i].name = decl->Semantic.Name; + ctx->inputs[i].sid = decl->Semantic.Index; + ctx->inputs[i].interpolate = decl->Interp.Interpolate; + ctx->inputs[i].location = decl->Interp.Location; + ctx->inputs[i].first = decl->Range.First; + ctx->inputs[i].layout_location = 0; + ctx->inputs[i].last = decl->Range.Last; + ctx->inputs[i].array_id = decl->Declaration.Array ? decl->Array.ArrayID : 0; + ctx->inputs[i].usage_mask = mask_temp = decl->Declaration.UsageMask; + u_bit_scan_consecutive_range(&mask_temp, &ctx->inputs[i].swizzle_offset, &ctx->inputs[i].num_components); + + ctx->inputs[i].glsl_predefined_no_emit = false; + ctx->inputs[i].glsl_no_index = false; + ctx->inputs[i].override_no_wm = ctx->inputs[i].num_components == 1; + ctx->inputs[i].glsl_gl_block = false; + + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT && + decl->Interp.Location == TGSI_INTERPOLATE_LOC_SAMPLE) { + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + ctx->has_sample_input = true; + } + + if (ctx->inputs[i].first != ctx->inputs[i].last) + require_glsl_ver(ctx, 150); + + switch (ctx->inputs[i].name) { + case TGSI_SEMANTIC_COLOR: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + if (ctx->glsl_ver_required < 140) { + if (decl->Semantic.Index == 0) + name_prefix = "gl_Color"; + else if (decl->Semantic.Index == 1) + name_prefix = "gl_SecondaryColor"; + ctx->inputs[i].glsl_no_index = true; + } else { + if (ctx->key->color_two_side) { + int j = ctx->num_inputs++; + if (ctx->num_inputs > ARRAY_SIZE(ctx->inputs)) + return false; + + ctx->inputs[j].name = TGSI_SEMANTIC_BCOLOR; + ctx->inputs[j].sid = decl->Semantic.Index; + ctx->inputs[j].interpolate = decl->Interp.Interpolate; + ctx->inputs[j].location = decl->Interp.Location; + ctx->inputs[j].first = decl->Range.First; + ctx->inputs[j].last = decl->Range.Last; + ctx->inputs[j].glsl_predefined_no_emit = false; + ctx->inputs[j].glsl_no_index = false; + ctx->inputs[j].override_no_wm = false; + + ctx->color_in_mask |= (1 << decl->Semantic.Index); + + if (ctx->front_face_emitted == false) { + int k = ctx->num_inputs++; + if (ctx->num_inputs > ARRAY_SIZE(ctx->inputs)) + return false; + + ctx->inputs[k].name = TGSI_SEMANTIC_FACE; + ctx->inputs[k].sid = 0; + ctx->inputs[k].interpolate = TGSI_INTERPOLATE_CONSTANT; + ctx->inputs[k].location = TGSI_INTERPOLATE_LOC_CENTER; + ctx->inputs[k].first = 0; + ctx->inputs[k].override_no_wm = false; + ctx->inputs[k].glsl_predefined_no_emit = true; + ctx->inputs[k].glsl_no_index = true; + } + add_two_side = true; + } + name_prefix = "ex"; + } + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_PRIMID: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY) { + name_prefix = "gl_PrimitiveIDIn"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].override_no_wm = true; + ctx->shader_req_bits |= SHADER_REQ_INTS; + break; + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + name_prefix = "gl_PrimitiveID"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + require_glsl_ver(ctx, 150); + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_VIEWPORT_INDEX: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].is_int = true; + ctx->inputs[i].override_no_wm = true; + name_prefix = "gl_ViewportIndex"; + if (ctx->glsl_ver_required >= 140) + ctx->shader_req_bits |= SHADER_REQ_LAYER; + + ctx->shader_req_bits |= SHADER_REQ_VIEWPORT_IDX; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_LAYER: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + name_prefix = "gl_Layer"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].is_int = true; + ctx->inputs[i].override_no_wm = true; + ctx->shader_req_bits |= SHADER_REQ_LAYER; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_PSIZE: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY || + iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL || + iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL) { + name_prefix = "gl_PointSize"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].override_no_wm = true; + ctx->inputs[i].glsl_gl_block = true; + ctx->shader_req_bits |= SHADER_REQ_PSIZE; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_CLIPDIST: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY || + iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL || + iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL) { + name_prefix = "gl_ClipDistance"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].glsl_gl_block = true; + ctx->num_in_clip_dist += 4 * (ctx->inputs[i].last - ctx->inputs[i].first + 1); + ctx->shader_req_bits |= SHADER_REQ_CLIP_DISTANCE; + if (ctx->inputs[i].last != ctx->inputs[i].first) + ctx->guest_sent_io_arrays = true; + break; + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + name_prefix = "gl_ClipDistance"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->num_in_clip_dist += 4 * (ctx->inputs[i].last - ctx->inputs[i].first + 1); + ctx->shader_req_bits |= SHADER_REQ_CLIP_DISTANCE; + if (ctx->inputs[i].last != ctx->inputs[i].first) + ctx->guest_sent_io_arrays = true; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_POSITION: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY || + iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL || + iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL) { + name_prefix = "gl_Position"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].glsl_gl_block = true; + break; + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + if (ctx->fs_pixel_center) { + name_prefix = "(gl_FragCoord - vec4(0.5, 0.5, 0.0, 0.0))"; + } else + name_prefix = "gl_FragCoord"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_FACE: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + if (ctx->front_face_emitted) { + ctx->num_inputs--; + return true; + } + name_prefix = "gl_FrontFacing"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->front_face_emitted = true; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_PATCH: + case TGSI_SEMANTIC_GENERIC: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + if (ctx->key->coord_replace & (1 << ctx->inputs[i].sid)) { + name_prefix = "vec4(gl_PointCoord.x, mix(1.0 - gl_PointCoord.y, gl_PointCoord.y, clamp(winsys_adjust_y, 0.0, 1.0)), 0.0, 1.0)"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].num_components = 4; + ctx->inputs[i].swizzle_offset = 0; + ctx->inputs[i].usage_mask = 0xf; + break; + } + } + + if (ctx->inputs[i].first != ctx->inputs[i].last || + ctx->inputs[i].array_id > 0) { + ctx->guest_sent_io_arrays = true; + } + + /* fallthrough */ + default: + name_prefix = get_stage_input_name_prefix(ctx, iter->processor.Processor); + break; + } + + if (ctx->inputs[i].glsl_no_index) + snprintf(ctx->inputs[i].glsl_name, 128, "%s", name_prefix); + else { + if (ctx->inputs[i].name == TGSI_SEMANTIC_FOG){ + ctx->inputs[i].usage_mask = 0xf; + ctx->inputs[i].num_components = 4; + ctx->inputs[i].swizzle_offset = 0; + ctx->inputs[i].override_no_wm = false; + snprintf(ctx->inputs[i].glsl_name, 128, "%s_f%d", name_prefix, ctx->inputs[i].sid); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_COLOR) + snprintf(ctx->inputs[i].glsl_name, 128, "%s_c%d", name_prefix, ctx->inputs[i].sid); + else if (ctx->inputs[i].name == TGSI_SEMANTIC_GENERIC) + snprintf(ctx->inputs[i].glsl_name, 128, "%s_g%dA%d", name_prefix, ctx->inputs[i].sid, ctx->inputs[i].array_id); + else if (ctx->inputs[i].name == TGSI_SEMANTIC_PATCH) + snprintf(ctx->inputs[i].glsl_name, 128, "%s_p%dA%d", name_prefix, ctx->inputs[i].sid, ctx->inputs[i].array_id); + else + snprintf(ctx->inputs[i].glsl_name, 128, "%s_%d", name_prefix, ctx->inputs[i].first); + } + if (add_two_side) { + snprintf(ctx->inputs[i + 1].glsl_name, 128, "%s_bc%d", name_prefix, ctx->inputs[i + 1].sid); + if (!ctx->front_face_emitted) { + snprintf(ctx->inputs[i + 2].glsl_name, 128, "%s", "gl_FrontFacing"); + ctx->front_face_emitted = true; + } + } + break; + case TGSI_FILE_OUTPUT: + for (uint32_t j = 0; j < ctx->num_outputs; j++) { + if (ctx->outputs[j].name == decl->Semantic.Name && + ctx->outputs[j].sid == decl->Semantic.Index && + ctx->outputs[j].first == decl->Range.First && + ctx->outputs[j].usage_mask == decl->Declaration.UsageMask && + ((!decl->Declaration.Array && ctx->outputs[j].array_id == 0) || + (ctx->outputs[j].array_id == decl->Array.ArrayID))) + return true; + } + i = ctx->num_outputs++; + if (ctx->num_outputs > ARRAY_SIZE(ctx->outputs)) + return false; + + ctx->outputs[i].name = decl->Semantic.Name; + ctx->outputs[i].sid = decl->Semantic.Index; + ctx->outputs[i].interpolate = decl->Interp.Interpolate; + ctx->outputs[i].invariant = decl->Declaration.Invariant; + ctx->outputs[i].precise = false; + ctx->outputs[i].first = decl->Range.First; + ctx->outputs[i].last = decl->Range.Last; + ctx->outputs[i].layout_location = 0; + ctx->outputs[i].array_id = decl->Declaration.Array ? decl->Array.ArrayID : 0; + ctx->outputs[i].usage_mask = mask_temp = decl->Declaration.UsageMask; + u_bit_scan_consecutive_range(&mask_temp, &ctx->outputs[i].swizzle_offset, &ctx->outputs[i].num_components); + ctx->outputs[i].glsl_predefined_no_emit = false; + ctx->outputs[i].glsl_no_index = false; + ctx->outputs[i].override_no_wm = ctx->outputs[i].num_components == 1; + ctx->outputs[i].is_int = false; + ctx->outputs[i].fbfetch_used = false; + + switch (ctx->outputs[i].name) { + case TGSI_SEMANTIC_POSITION: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX || + iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY || + iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL || + iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL) { + name_prefix = "gl_Position"; + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + if (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) + ctx->outputs[i].glsl_gl_block = true; + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + name_prefix = "gl_FragDepth"; + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + } + break; + case TGSI_SEMANTIC_STENCIL: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + name_prefix = "gl_FragStencilRefARB"; + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->outputs[i].is_int = true; + ctx->shader_req_bits |= (SHADER_REQ_INTS | SHADER_REQ_STENCIL_EXPORT); + } + break; + case TGSI_SEMANTIC_CLIPDIST: + ctx->shader_req_bits |= SHADER_REQ_CLIP_DISTANCE; + name_prefix = "gl_ClipDistance"; + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->num_clip_dist += 4 * (ctx->outputs[i].last - ctx->outputs[i].first + 1); + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX && + (ctx->key->gs_present || ctx->key->tcs_present)) + require_glsl_ver(ctx, 150); + if (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) + ctx->outputs[i].glsl_gl_block = true; + if (ctx->outputs[i].last != ctx->outputs[i].first) + ctx->guest_sent_io_arrays = true; + break; + case TGSI_SEMANTIC_CLIPVERTEX: + name_prefix = "gl_ClipVertex"; + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->outputs[i].invariant = false; + ctx->outputs[i].precise = false; + if (ctx->glsl_ver_required >= 140) + ctx->has_clipvertex = true; + break; + case TGSI_SEMANTIC_SAMPLEMASK: + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->outputs[i].is_int = true; + ctx->shader_req_bits |= (SHADER_REQ_INTS | SHADER_REQ_SAMPLE_SHADING); + name_prefix = "gl_SampleMask"; + break; + } + break; + case TGSI_SEMANTIC_COLOR: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX) { + if (ctx->glsl_ver_required < 140) { + ctx->outputs[i].glsl_no_index = true; + if (ctx->outputs[i].sid == 0) + name_prefix = "gl_FrontColor"; + else if (ctx->outputs[i].sid == 1) + name_prefix = "gl_FrontSecondaryColor"; + } else + name_prefix = "ex"; + break; + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT && + ctx->key->fs_logicop_enabled) { + name_prefix = "fsout_tmp"; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_BCOLOR: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX) { + if (ctx->glsl_ver_required < 140) { + ctx->outputs[i].glsl_no_index = true; + if (ctx->outputs[i].sid == 0) + name_prefix = "gl_BackColor"; + else if (ctx->outputs[i].sid == 1) + name_prefix = "gl_BackSecondaryColor"; + break; + } else + name_prefix = "ex"; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_PSIZE: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX || + iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY || + iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL || + iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->shader_req_bits |= SHADER_REQ_PSIZE; + name_prefix = "gl_PointSize"; + if (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) + ctx->outputs[i].glsl_gl_block = true; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_LAYER: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->outputs[i].is_int = true; + name_prefix = "gl_Layer"; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_PRIMID: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->outputs[i].is_int = true; + name_prefix = "gl_PrimitiveID"; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_VIEWPORT_INDEX: + if (iter->processor.Processor == TGSI_PROCESSOR_GEOMETRY) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + ctx->outputs[i].is_int = true; + name_prefix = "gl_ViewportIndex"; + ctx->shader_req_bits |= SHADER_REQ_VIEWPORT_IDX; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_TESSOUTER: + if (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + name_prefix = "gl_TessLevelOuter"; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_TESSINNER: + if (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) { + ctx->outputs[i].glsl_predefined_no_emit = true; + ctx->outputs[i].glsl_no_index = true; + ctx->outputs[i].override_no_wm = true; + name_prefix = "gl_TessLevelInner"; + break; + } + /* fallthrough */ + case TGSI_SEMANTIC_PATCH: + case TGSI_SEMANTIC_GENERIC: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX) + if (ctx->outputs[i].name == TGSI_SEMANTIC_GENERIC) + color_offset = -1; + + if (ctx->outputs[i].first != ctx->outputs[i].last || + ctx->outputs[i].array_id > 0) { + ctx->guest_sent_io_arrays = true; + } + /* fallthrough */ + default: + name_prefix = get_stage_output_name_prefix(iter->processor.Processor); + break; + } + + if (ctx->outputs[i].glsl_no_index) + snprintf(ctx->outputs[i].glsl_name, 64, "%s", name_prefix); + else { + if (ctx->outputs[i].name == TGSI_SEMANTIC_FOG) { + ctx->outputs[i].usage_mask = 0xf; + ctx->outputs[i].num_components = 4; + ctx->outputs[i].swizzle_offset = 0; + ctx->outputs[i].override_no_wm = false; + snprintf(ctx->outputs[i].glsl_name, 64, "%s_f%d", name_prefix, ctx->outputs[i].sid); + } else if (ctx->outputs[i].name == TGSI_SEMANTIC_COLOR) + snprintf(ctx->outputs[i].glsl_name, 64, "%s_c%d", name_prefix, ctx->outputs[i].sid); + else if (ctx->outputs[i].name == TGSI_SEMANTIC_BCOLOR) + snprintf(ctx->outputs[i].glsl_name, 64, "%s_bc%d", name_prefix, ctx->outputs[i].sid); + else if (ctx->outputs[i].name == TGSI_SEMANTIC_PATCH) + snprintf(ctx->outputs[i].glsl_name, 64, "%s_p%dA%d", name_prefix, ctx->outputs[i].sid, ctx->outputs[i].array_id); + else if (ctx->outputs[i].name == TGSI_SEMANTIC_GENERIC) + snprintf(ctx->outputs[i].glsl_name, 64, "%s_g%dA%d", name_prefix, ctx->outputs[i].sid, ctx->outputs[i].array_id); + else + snprintf(ctx->outputs[i].glsl_name, 64, "%s_%d", name_prefix, ctx->outputs[i].first + color_offset); + + } + break; + case TGSI_FILE_TEMPORARY: + if (!allocate_temp_range(ctx, decl->Range.First, decl->Range.Last, + decl->Array.ArrayID)) + return false; + break; + case TGSI_FILE_SAMPLER: + ctx->samplers_used |= (1 << decl->Range.Last); + break; + case TGSI_FILE_SAMPLER_VIEW: + if (decl->Range.Last >= ARRAY_SIZE(ctx->samplers)) + return false; + if (!add_samplers(ctx, decl->Range.First, decl->Range.Last, decl->SamplerView.Resource, decl->SamplerView.ReturnTypeX)) + return false; + break; + case TGSI_FILE_IMAGE: + ctx->shader_req_bits |= SHADER_REQ_IMAGE_LOAD_STORE; + if (decl->Range.Last >= ARRAY_SIZE(ctx->images)) + return false; + if (!add_images(ctx, decl->Range.First, decl->Range.Last, &decl->Image)) + return false; + break; + case TGSI_FILE_BUFFER: + if (decl->Range.First >= 32) + return false; + ctx->ssbo_used_mask |= (1 << decl->Range.First); + if (decl->Declaration.Atomic) { + if (decl->Range.First < ctx->ssbo_atomic_array_base) + ctx->ssbo_atomic_array_base = decl->Range.First; + ctx->ssbo_atomic_mask |= (1 << decl->Range.First); + } else { + if (decl->Range.First < ctx->ssbo_array_base) + ctx->ssbo_array_base = decl->Range.First; + } + break; + case TGSI_FILE_CONSTANT: + if (decl->Declaration.Dimension && decl->Dim.Index2D != 0) { + if (decl->Dim.Index2D > 31) + return false; + if (ctx->ubo_used_mask & (1 << decl->Dim.Index2D)) + return false; + ctx->ubo_used_mask |= (1 << decl->Dim.Index2D); + ctx->ubo_sizes[decl->Dim.Index2D] = decl->Range.Last + 1; + } else { + /* if we have a normal single const set then ubo base should be 1 */ + ctx->ubo_base = 1; + if (decl->Range.Last) { + if (decl->Range.Last + 1 > ctx->num_consts) + ctx->num_consts = decl->Range.Last + 1; + } else + ctx->num_consts++; + } + break; + case TGSI_FILE_ADDRESS: + ctx->num_address = decl->Range.Last + 1; + break; + case TGSI_FILE_SYSTEM_VALUE: + i = ctx->num_system_values++; + if (ctx->num_system_values > ARRAY_SIZE(ctx->system_values)) + return false; + + ctx->system_values[i].name = decl->Semantic.Name; + ctx->system_values[i].sid = decl->Semantic.Index; + ctx->system_values[i].glsl_predefined_no_emit = true; + ctx->system_values[i].glsl_no_index = true; + ctx->system_values[i].override_no_wm = true; + ctx->system_values[i].first = decl->Range.First; + if (decl->Semantic.Name == TGSI_SEMANTIC_INSTANCEID) { + name_prefix = "gl_InstanceID"; + ctx->shader_req_bits |= SHADER_REQ_INSTANCE_ID | SHADER_REQ_INTS; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_VERTEXID) { + name_prefix = "gl_VertexID"; + ctx->shader_req_bits |= SHADER_REQ_INTS; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_HELPER_INVOCATION) { + name_prefix = "gl_HelperInvocation"; + ctx->shader_req_bits |= SHADER_REQ_ES31_COMPAT; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_SAMPLEID) { + name_prefix = "gl_SampleID"; + ctx->shader_req_bits |= (SHADER_REQ_SAMPLE_SHADING | SHADER_REQ_INTS); + } else if (decl->Semantic.Name == TGSI_SEMANTIC_SAMPLEPOS) { + name_prefix = "gl_SamplePosition"; + ctx->shader_req_bits |= SHADER_REQ_SAMPLE_SHADING; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_INVOCATIONID) { + name_prefix = "gl_InvocationID"; + ctx->shader_req_bits |= (SHADER_REQ_INTS | SHADER_REQ_GPU_SHADER5); + } else if (decl->Semantic.Name == TGSI_SEMANTIC_SAMPLEMASK) { + name_prefix = "gl_SampleMaskIn[0]"; + ctx->shader_req_bits |= (SHADER_REQ_INTS | SHADER_REQ_GPU_SHADER5); + } else if (decl->Semantic.Name == TGSI_SEMANTIC_PRIMID) { + name_prefix = "gl_PrimitiveID"; + ctx->shader_req_bits |= (SHADER_REQ_INTS | SHADER_REQ_GPU_SHADER5); + } else if (decl->Semantic.Name == TGSI_SEMANTIC_TESSCOORD) { + name_prefix = "gl_TessCoord"; + ctx->system_values[i].override_no_wm = false; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_VERTICESIN) { + ctx->shader_req_bits |= SHADER_REQ_INTS; + name_prefix = "gl_PatchVerticesIn"; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_TESSOUTER) { + name_prefix = "gl_TessLevelOuter"; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_TESSINNER) { + name_prefix = "gl_TessLevelInner"; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_THREAD_ID) { + name_prefix = "gl_LocalInvocationID"; + ctx->system_values[i].override_no_wm = false; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_BLOCK_ID) { + name_prefix = "gl_WorkGroupID"; + ctx->system_values[i].override_no_wm = false; + } else if (decl->Semantic.Name == TGSI_SEMANTIC_GRID_SIZE) { + name_prefix = "gl_NumWorkGroups"; + ctx->system_values[i].override_no_wm = false; + } else { + name_prefix = "unknown"; + } + snprintf(ctx->system_values[i].glsl_name, 64, "%s", name_prefix); + break; + case TGSI_FILE_MEMORY: + ctx->has_file_memory = true; + break; + case TGSI_FILE_HW_ATOMIC: + if (ctx->num_abo >= ARRAY_SIZE(ctx->abo_idx)) + return false; + ctx->abo_idx[ctx->num_abo] = decl->Dim.Index2D; + ctx->abo_sizes[ctx->num_abo] = decl->Range.Last - decl->Range.First + 1; + ctx->abo_offsets[ctx->num_abo] = decl->Range.First; + ctx->num_abo++; + break; + } + + return true; +} + +static boolean +iter_property(struct tgsi_iterate_context *iter, + struct tgsi_full_property *prop) +{ + struct dump_ctx *ctx = (struct dump_ctx *) iter; + + switch (prop->Property.PropertyName) { + case TGSI_PROPERTY_FS_COLOR0_WRITES_ALL_CBUFS: + if (prop->u[0].Data == 1) + ctx->write_all_cbufs = true; + break; + case TGSI_PROPERTY_FS_COORD_ORIGIN: + ctx->fs_coord_origin = prop->u[0].Data; + break; + case TGSI_PROPERTY_FS_COORD_PIXEL_CENTER: + ctx->fs_pixel_center = prop->u[0].Data; + break; + case TGSI_PROPERTY_FS_DEPTH_LAYOUT: + break; + case TGSI_PROPERTY_GS_INPUT_PRIM: + ctx->gs_in_prim = prop->u[0].Data; + break; + case TGSI_PROPERTY_GS_OUTPUT_PRIM: + ctx->gs_out_prim = prop->u[0].Data; + break; + case TGSI_PROPERTY_GS_MAX_OUTPUT_VERTICES: + ctx->gs_max_out_verts = prop->u[0].Data; + break; + case TGSI_PROPERTY_GS_INVOCATIONS: + ctx->gs_num_invocations = prop->u[0].Data; + break; + case TGSI_PROPERTY_NUM_CLIPDIST_ENABLED: + ctx->shader_req_bits |= SHADER_REQ_CLIP_DISTANCE; + ctx->num_clip_dist_prop = prop->u[0].Data; + break; + case TGSI_PROPERTY_NUM_CULLDIST_ENABLED: + ctx->num_cull_dist_prop = prop->u[0].Data; + break; + case TGSI_PROPERTY_TCS_VERTICES_OUT: + ctx->tcs_vertices_out = prop->u[0].Data; + break; + case TGSI_PROPERTY_TES_PRIM_MODE: + ctx->tes_prim_mode = prop->u[0].Data; + break; + case TGSI_PROPERTY_TES_SPACING: + ctx->tes_spacing = prop->u[0].Data; + break; + case TGSI_PROPERTY_TES_VERTEX_ORDER_CW: + ctx->tes_vertex_order = prop->u[0].Data; + break; + case TGSI_PROPERTY_TES_POINT_MODE: + ctx->tes_point_mode = prop->u[0].Data; + break; + case TGSI_PROPERTY_FS_EARLY_DEPTH_STENCIL: + ctx->early_depth_stencil = prop->u[0].Data > 0; + if (ctx->early_depth_stencil) { + require_glsl_ver(ctx, 150); + ctx->shader_req_bits |= SHADER_REQ_IMAGE_LOAD_STORE; + } + break; + case TGSI_PROPERTY_CS_FIXED_BLOCK_WIDTH: + ctx->local_cs_block_size[0] = prop->u[0].Data; + break; + case TGSI_PROPERTY_CS_FIXED_BLOCK_HEIGHT: + ctx->local_cs_block_size[1] = prop->u[0].Data; + break; + case TGSI_PROPERTY_CS_FIXED_BLOCK_DEPTH: + ctx->local_cs_block_size[2] = prop->u[0].Data; + break; + default: + return false; + } + + return true; +} + +static boolean +iter_immediate( + struct tgsi_iterate_context *iter, + struct tgsi_full_immediate *imm ) +{ + struct dump_ctx *ctx = (struct dump_ctx *) iter; + int i; + uint32_t first = ctx->num_imm; + + if (first >= ARRAY_SIZE(ctx->imm)) + return false; + + ctx->imm[first].type = imm->Immediate.DataType; + for (i = 0; i < 4; i++) { + if (imm->Immediate.DataType == TGSI_IMM_FLOAT32) { + ctx->imm[first].val[i].f = imm->u[i].Float; + } else if (imm->Immediate.DataType == TGSI_IMM_UINT32 || + imm->Immediate.DataType == TGSI_IMM_FLOAT64) { + ctx->shader_req_bits |= SHADER_REQ_INTS; + ctx->imm[first].val[i].ui = imm->u[i].Uint; + } else if (imm->Immediate.DataType == TGSI_IMM_INT32) { + ctx->shader_req_bits |= SHADER_REQ_INTS; + ctx->imm[first].val[i].i = imm->u[i].Int; + } + } + ctx->num_imm++; + return true; +} + +static char get_swiz_char(int swiz) +{ + switch(swiz){ + case TGSI_SWIZZLE_X: return 'x'; + case TGSI_SWIZZLE_Y: return 'y'; + case TGSI_SWIZZLE_Z: return 'z'; + case TGSI_SWIZZLE_W: return 'w'; + default: return 0; + } +} + +static void emit_cbuf_writes(struct dump_ctx *ctx) +{ + int i; + + for (i = ctx->num_outputs; i < ctx->cfg->max_draw_buffers; i++) { + emit_buff(ctx, "fsout_c%d = fsout_c0;\n", i); + } +} + +static void emit_a8_swizzle(struct dump_ctx *ctx) +{ + emit_buf(ctx, "fsout_c0.x = fsout_c0.w;\n"); +} + +static const char *atests[PIPE_FUNC_ALWAYS + 1] = { + "false", + "<", + "==", + "<=", + ">", + "!=", + ">=", + "true" +}; + +static void emit_alpha_test(struct dump_ctx *ctx) +{ + char comp_buf[128]; + + if (!ctx->num_outputs) + return; + + if (!ctx->write_all_cbufs) { + /* only emit alpha stanza if first output is 0 */ + if (ctx->outputs[0].sid != 0) + return; + } + switch (ctx->key->alpha_test) { + case PIPE_FUNC_NEVER: + case PIPE_FUNC_ALWAYS: + snprintf(comp_buf, 128, "%s", atests[ctx->key->alpha_test]); + break; + case PIPE_FUNC_LESS: + case PIPE_FUNC_EQUAL: + case PIPE_FUNC_LEQUAL: + case PIPE_FUNC_GREATER: + case PIPE_FUNC_NOTEQUAL: + case PIPE_FUNC_GEQUAL: + snprintf(comp_buf, 128, "%s %s %f", "fsout_c0.w", atests[ctx->key->alpha_test], ctx->key->alpha_ref_val); + break; + default: + set_buf_error(ctx); + return; + } + + emit_buff(ctx, "if (!(%s)) {\n\tdiscard;\n}\n", comp_buf); +} + +static void emit_pstipple_pass(struct dump_ctx *ctx) +{ + emit_buf(ctx, "stip_temp = texture(pstipple_sampler, vec2(gl_FragCoord.x / 32.0, gl_FragCoord.y / 32.0)).x;\n"); + emit_buf(ctx, "if (stip_temp > 0.0) {\n\tdiscard;\n}\n"); +} + +static void emit_color_select(struct dump_ctx *ctx) +{ + if (!ctx->key->color_two_side || !(ctx->color_in_mask & 0x3)) + return; + + if (ctx->color_in_mask & 1) + emit_buf(ctx, "realcolor0 = gl_FrontFacing ? ex_c0 : ex_bc0;\n"); + + if (ctx->color_in_mask & 2) + emit_buf(ctx, "realcolor1 = gl_FrontFacing ? ex_c1 : ex_bc1;\n"); +} + +static void emit_prescale(struct dump_ctx *ctx) +{ + emit_buf(ctx, "gl_Position.y = gl_Position.y * winsys_adjust_y;\n"); +} + +static void prepare_so_movs(struct dump_ctx *ctx) +{ + uint32_t i; + for (i = 0; i < ctx->so->num_outputs; i++) { + ctx->write_so_outputs[i] = true; + if (ctx->so->output[i].start_component != 0) + continue; + if (ctx->so->output[i].num_components != 4) + continue; + if (ctx->outputs[ctx->so->output[i].register_index].name == TGSI_SEMANTIC_CLIPDIST) + continue; + if (ctx->outputs[ctx->so->output[i].register_index].name == TGSI_SEMANTIC_POSITION) + continue; + + ctx->outputs[ctx->so->output[i].register_index].stream = ctx->so->output[i].stream; + if (ctx->prog_type == TGSI_PROCESSOR_GEOMETRY && ctx->so->output[i].stream) + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + + ctx->write_so_outputs[i] = false; + } +} + +static const struct vrend_shader_io *get_io_slot(const struct vrend_shader_io *slots, unsigned nslots, int idx) +{ + const struct vrend_shader_io *result = slots; + for (unsigned i = 0; i < nslots; ++i, ++result) { + if ((result->first <= idx) && (result->last >= idx)) + return result; + } + assert(0 && "Output not found"); + return NULL; +} + +static inline void +get_blockname(char outvar[64], const char *stage_prefix, const struct vrend_shader_io *io) +{ + snprintf(outvar, 64, "block_%sg%dA%d", stage_prefix, io->sid, io->array_id); +} + +static inline void +get_blockvarname(char outvar[64], const char *stage_prefix, const struct vrend_shader_io *io, const char *postfix) +{ + snprintf(outvar, 64, "%sg%dA%d_%x%s", stage_prefix, io->first, io->array_id, io->usage_mask, postfix); +} + +static void get_so_name(struct dump_ctx *ctx, bool from_block, const struct vrend_shader_io *output, int index, char out_var[255], char *wm) +{ + if (output->first == output->last || output->name != TGSI_SEMANTIC_GENERIC) + snprintf(out_var, 255, "%s%s", output->glsl_name, wm); + else { + if ((output->name == TGSI_SEMANTIC_GENERIC) && prefer_generic_io_block(ctx, io_out)) { + char blockname[64]; + const char *stage_prefix = get_stage_output_name_prefix(ctx->prog_type); + if (from_block) + get_blockname(blockname, stage_prefix, output); + else + get_blockvarname(blockname, stage_prefix, output, ""); + snprintf(out_var, 255, "%s.%s[%d]%s", blockname, output->glsl_name, index - output->first, wm); + } else { + snprintf(out_var, 255, "%s[%d]%s", output->glsl_name, index - output->first, wm); + } + } +} + +static void emit_so_movs(struct dump_ctx *ctx) +{ + uint32_t i, j; + char outtype[15] = ""; + char writemask[6]; + + if (ctx->so->num_outputs >= PIPE_MAX_SO_OUTPUTS) { + set_buf_error(ctx); + return; + } + + for (i = 0; i < ctx->so->num_outputs; i++) { + const struct vrend_shader_io *output = get_io_slot(&ctx->outputs[0], ctx->num_outputs, ctx->so->output[i].register_index); + if (ctx->so->output[i].start_component != 0) { + int wm_idx = 0; + writemask[wm_idx++] = '.'; + for (j = 0; j < ctx->so->output[i].num_components; j++) { + unsigned idx = ctx->so->output[i].start_component + j; + if (idx >= 4) + break; + if (idx <= 2) + writemask[wm_idx++] = 'x' + idx; + else + writemask[wm_idx++] = 'w'; + } + writemask[wm_idx] = '\0'; + } else + writemask[0] = 0; + + if (!ctx->write_so_outputs[i]) { + if (ctx->so_names[i]) + free(ctx->so_names[i]); + if (ctx->so->output[i].register_index > ctx->num_outputs) + ctx->so_names[i] = NULL; + else if (ctx->outputs[ctx->so->output[i].register_index].name == TGSI_SEMANTIC_CLIPVERTEX && ctx->has_clipvertex) { + ctx->so_names[i] = strdup("clipv_tmp"); + ctx->has_clipvertex_so = true; + } else { + char out_var[255]; + get_so_name(ctx, true, output, ctx->so->output[i].register_index, out_var, ""); + ctx->so_names[i] = strdup(out_var); + } + } else { + char ntemp[8]; + snprintf(ntemp, 8, "tfout%d", i); + ctx->so_names[i] = strdup(ntemp); + } + if (ctx->so->output[i].num_components == 1) { + if (ctx->outputs[ctx->so->output[i].register_index].is_int) + snprintf(outtype, 15, "intBitsToFloat"); + else + snprintf(outtype, 15, "float"); + } else + snprintf(outtype, 15, "vec%d", ctx->so->output[i].num_components); + + if (ctx->so->output[i].register_index >= 255) + continue; + + if (output->name == TGSI_SEMANTIC_CLIPDIST) { + if (output->first == output->last) + emit_buff(ctx, "tfout%d = %s(clip_dist_temp[%d]%s);\n", i, outtype, output->sid, + writemask); + else + emit_buff(ctx, "tfout%d = %s(clip_dist_temp[%d]%s);\n", i, outtype, + output->sid + ctx->so->output[i].register_index - output->first, + writemask); + } else { + if (ctx->write_so_outputs[i]) { + char out_var[255]; + if (ctx->so->output[i].need_temp || ctx->prog_type == TGSI_PROCESSOR_GEOMETRY || + output->glsl_predefined_no_emit) { + get_so_name(ctx, false, output, ctx->so->output[i].register_index, out_var, writemask); + emit_buff(ctx, "tfout%d = %s(%s);\n", i, outtype, out_var); + } else { + get_so_name(ctx, true, output, ctx->so->output[i].register_index, out_var, writemask); + ctx->so_names[i] = strdup(out_var); + } + } + } + } +} + +static void emit_clip_dist_movs(struct dump_ctx *ctx) +{ + int i; + bool has_prop = (ctx->num_clip_dist_prop + ctx->num_cull_dist_prop) > 0; + int ndists; + const char *prefix=""; + + if (ctx->prog_type == PIPE_SHADER_TESS_CTRL) + prefix = "gl_out[gl_InvocationID]."; + if (ctx->num_clip_dist == 0 && ctx->key->clip_plane_enable) { + for (i = 0; i < 8; i++) { + emit_buff(ctx, "%sgl_ClipDistance[%d] = dot(%s, clipp[%d]);\n", prefix, i, ctx->has_clipvertex ? "clipv_tmp" : "gl_Position", i); + } + return; + } + ndists = ctx->num_clip_dist; + if (has_prop) + ndists = ctx->num_clip_dist_prop + ctx->num_cull_dist_prop; + for (i = 0; i < ndists; i++) { + int clipidx = i < 4 ? 0 : 1; + char swiz = i & 3; + char wm = 0; + switch (swiz) { + default: + case 0: wm = 'x'; break; + case 1: wm = 'y'; break; + case 2: wm = 'z'; break; + case 3: wm = 'w'; break; + } + bool is_cull = false; + if (has_prop) { + if (i >= ctx->num_clip_dist_prop && i < ctx->num_clip_dist_prop + ctx->num_cull_dist_prop) + is_cull = true; + } + const char *clip_cull = is_cull ? "Cull" : "Clip"; + emit_buff(ctx, "%sgl_%sDistance[%d] = clip_dist_temp[%d].%c;\n", prefix, clip_cull, + is_cull ? i - ctx->num_clip_dist_prop : i, clipidx, wm); + } +} + +#define emit_arit_op2(op) emit_buff(ctx, "%s = %s(%s((%s %s %s))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], op, srcs[1], writemask) +#define emit_op1(op) emit_buff(ctx, "%s = %s(%s(%s(%s))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), op, srcs[0], writemask) +#define emit_compare(op) emit_buff(ctx, "%s = %s(%s((%s(%s(%s), %s(%s))))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), op, get_string(sinfo.svec4), srcs[0], get_string(sinfo.svec4), srcs[1], writemask) + +#define emit_ucompare(op) emit_buff(ctx, "%s = %s(uintBitsToFloat(%s(%s(%s(%s), %s(%s))%s) * %s(0xffffffff)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.udstconv), op, get_string(sinfo.svec4), srcs[0], get_string(sinfo.svec4), srcs[1], writemask, get_string(dinfo.udstconv)) + +static void handle_vertex_proc_exit(struct dump_ctx *ctx) +{ + if (ctx->so && !ctx->key->gs_present && !ctx->key->tes_present) + emit_so_movs(ctx); + + emit_clip_dist_movs(ctx); + + if (!ctx->key->gs_present && !ctx->key->tes_present) + emit_prescale(ctx); +} + +static void emit_fragment_logicop(struct dump_ctx *ctx) +{ + char src[PIPE_MAX_COLOR_BUFS][64]; + char src_fb[PIPE_MAX_COLOR_BUFS][64]; + double scale[PIPE_MAX_COLOR_BUFS]; + int mask[PIPE_MAX_COLOR_BUFS]; + char full_op[PIPE_MAX_COLOR_BUFS][128]; + + for (unsigned i = 0; i < ctx->num_outputs; i++) { + mask[i] = (1 << ctx->key->surface_component_bits[i]) - 1; + scale[i] = mask[i]; + switch (ctx->key->fs_logicop_func) { + case PIPE_LOGICOP_INVERT: + snprintf(src_fb[i], 64, "ivec4(%f * fsout_c%d + 0.5)", scale[i], i); + break; + case PIPE_LOGICOP_NOR: + case PIPE_LOGICOP_AND_INVERTED: + case PIPE_LOGICOP_AND_REVERSE: + case PIPE_LOGICOP_XOR: + case PIPE_LOGICOP_NAND: + case PIPE_LOGICOP_AND: + case PIPE_LOGICOP_EQUIV: + case PIPE_LOGICOP_OR_INVERTED: + case PIPE_LOGICOP_OR_REVERSE: + case PIPE_LOGICOP_OR: + snprintf(src_fb[i], 64, "ivec4(%f * fsout_c%d + 0.5)", scale[i], i); + /* fallthrough */ + case PIPE_LOGICOP_COPY_INVERTED: + snprintf(src[i], 64, "ivec4(%f * fsout_tmp_c%d + 0.5)", scale[i], i); + break; + case PIPE_LOGICOP_COPY: + case PIPE_LOGICOP_NOOP: + case PIPE_LOGICOP_CLEAR: + case PIPE_LOGICOP_SET: + break; + } + } + + for (unsigned i = 0; i < ctx->num_outputs; i++) { + switch (ctx->key->fs_logicop_func) { + case PIPE_LOGICOP_CLEAR: snprintf(full_op[i], 128, "%s", "vec4(0)"); break; + case PIPE_LOGICOP_NOOP: full_op[i][0]= 0; break; + case PIPE_LOGICOP_SET: snprintf(full_op[i], 128, "%s", "vec4(1)"); break; + case PIPE_LOGICOP_COPY: snprintf(full_op[i], 128, "fsout_tmp_c%d", i); break; + case PIPE_LOGICOP_COPY_INVERTED: snprintf(full_op[i], 128, "~%s", src[i]); break; + case PIPE_LOGICOP_INVERT: snprintf(full_op[i], 128, "~%s", src_fb[i]); break; + case PIPE_LOGICOP_AND: snprintf(full_op[i], 128, "%s & %s", src[i], src_fb[i]); break; + case PIPE_LOGICOP_NAND: snprintf(full_op[i], 128, "~( %s & %s )", src[i], src_fb[i]); break; + case PIPE_LOGICOP_NOR: snprintf(full_op[i], 128, "~( %s | %s )", src[i], src_fb[i]); break; + case PIPE_LOGICOP_AND_INVERTED: snprintf(full_op[i], 128, "~%s & %s", src[i], src_fb[i]); break; + case PIPE_LOGICOP_AND_REVERSE: snprintf(full_op[i], 128, "%s & ~%s", src[i], src_fb[i]); break; + case PIPE_LOGICOP_XOR: snprintf(full_op[i], 128, "%s ^%s", src[i], src_fb[i]); break; + case PIPE_LOGICOP_EQUIV: snprintf(full_op[i], 128, "~( %s ^ %s )", src[i], src_fb[i]); break; + case PIPE_LOGICOP_OR_INVERTED: snprintf(full_op[i], 128, "~%s | %s", src[i], src_fb[i]); break; + case PIPE_LOGICOP_OR_REVERSE: snprintf(full_op[i], 128, "%s | ~%s", src[i], src_fb[i]); break; + case PIPE_LOGICOP_OR: snprintf(full_op[i], 128, "%s | %s", src[i], src_fb[i]); break; + + } + } + + for (unsigned i = 0; i < ctx->num_outputs; i++) { + switch (ctx->key->fs_logicop_func) { + case PIPE_LOGICOP_NOOP: + break; + case PIPE_LOGICOP_COPY: + case PIPE_LOGICOP_CLEAR: + case PIPE_LOGICOP_SET: + emit_buff(ctx, "fsout_c%d = %s;\n", i, full_op[i]); + break; + default: + emit_buff(ctx, "fsout_c%d = vec4((%s) & %d) / %f;\n", i, full_op[i], mask[i], scale[i]); + } + } +} + +static void emit_cbuf_swizzle(struct dump_ctx *ctx) +{ + for (uint i = 0; i < ctx->num_outputs; i++) { + if (ctx->key->fs_swizzle_output_rgb_to_bgr & (1 << i)) { + emit_buff(ctx, "fsout_c%d = fsout_c%d.zyxw;\n", i, i); + } + } +} + +static void handle_fragment_proc_exit(struct dump_ctx *ctx) +{ + if (ctx->key->pstipple_tex) + emit_pstipple_pass(ctx); + + if (ctx->key->cbufs_are_a8_bitmask) + emit_a8_swizzle(ctx); + + if (ctx->key->add_alpha_test) + emit_alpha_test(ctx); + + if (ctx->key->fs_logicop_enabled) + emit_fragment_logicop(ctx); + + if (ctx->key->fs_swizzle_output_rgb_to_bgr) + emit_cbuf_swizzle(ctx); + + if (ctx->write_all_cbufs) + emit_cbuf_writes(ctx); +} + +static void set_texture_reqs(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + uint32_t sreg_index) +{ + if (sreg_index >= ARRAY_SIZE(ctx->samplers)) { + set_buf_error(ctx); + return; + } + ctx->samplers[sreg_index].tgsi_sampler_type = inst->Texture.Texture; + + ctx->shader_req_bits |= samplertype_to_req_bits(inst->Texture.Texture); + + if (ctx->cfg->glsl_version >= 140) + if (ctx->shader_req_bits & (SHADER_REQ_SAMPLER_RECT | + SHADER_REQ_SAMPLER_BUF)) + require_glsl_ver(ctx, 140); +} + +/* size queries are pretty much separate */ +static void emit_txq(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + uint32_t sreg_index, + const char *srcs[4], + const char *dst, + const char *writemask) +{ + unsigned twm = TGSI_WRITEMASK_NONE; + char bias[128] = ""; + const int sampler_index = 1; + enum vrend_type_qualifier dtypeprefix = INT_BITS_TO_FLOAT; + + set_texture_reqs(ctx, inst, sreg_index); + + /* No LOD for these texture types, but on GLES we emulate RECT by using + * a normal 2D texture, so we have to give LOD 0 */ + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_SHADOWRECT: + snprintf(bias, 128, ", 0"); + break; + /* fallthrough */ + case TGSI_TEXTURE_BUFFER: + case TGSI_TEXTURE_2D_MSAA: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + break; + default: + snprintf(bias, 128, ", int(%s.w)", srcs[0]); + } + + /* need to emit a textureQueryLevels */ + if (inst->Dst[0].Register.WriteMask & 0x8) { + if (inst->Texture.Texture != TGSI_TEXTURE_BUFFER && + inst->Texture.Texture != TGSI_TEXTURE_RECT && + inst->Texture.Texture != TGSI_TEXTURE_2D_MSAA && + inst->Texture.Texture != TGSI_TEXTURE_2D_ARRAY_MSAA) { + ctx->shader_req_bits |= SHADER_REQ_TXQ_LEVELS; + if (inst->Dst[0].Register.WriteMask & 0x7) + twm = TGSI_WRITEMASK_W; + emit_buff(ctx, "%s%s = %s(textureQueryLevels(%s));\n", dst, + get_wm_string(twm), get_string(dtypeprefix), + srcs[sampler_index]); + } + + if (inst->Dst[0].Register.WriteMask & 0x7) { + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_BUFFER: + case TGSI_TEXTURE_SHADOW1D: + twm = TGSI_WRITEMASK_X; + break; + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_2D_MSAA: + twm = TGSI_WRITEMASK_XY; + break; + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + case TGSI_TEXTURE_2D_ARRAY_MSAA: + twm = TGSI_WRITEMASK_XYZ; + break; + } + } + } + + if (inst->Dst[0].Register.WriteMask & 0x7) { + bool txq_returns_vec = (inst->Texture.Texture != TGSI_TEXTURE_BUFFER) && + ((inst->Texture.Texture != TGSI_TEXTURE_1D && + inst->Texture.Texture != TGSI_TEXTURE_SHADOW1D)); + + if ((inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY || + inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D_ARRAY)) { + writemask = ".xz"; + } + + emit_buff(ctx, "%s%s = %s(textureSize(%s%s))%s;\n", dst, + get_wm_string(twm), get_string(dtypeprefix), + srcs[sampler_index], bias, txq_returns_vec ? writemask : ""); + } +} + +/* sample queries are pretty much separate */ +static void emit_txqs(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + uint32_t sreg_index, + const char *srcs[4], + const char *dst) +{ + const int sampler_index = 0; + enum vrend_type_qualifier dtypeprefix = INT_BITS_TO_FLOAT; + + ctx->shader_req_bits |= SHADER_REQ_TXQS; + set_texture_reqs(ctx, inst, sreg_index); + + if (inst->Texture.Texture != TGSI_TEXTURE_2D_MSAA && + inst->Texture.Texture != TGSI_TEXTURE_2D_ARRAY_MSAA) { + set_buf_error(ctx); + return; + } + + emit_buff(ctx, "%s = %s(textureSamples(%s));\n", dst, + get_string(dtypeprefix), srcs[sampler_index]); +} + +static const char *get_tex_inst_ext(struct tgsi_full_instruction *inst) +{ + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_LODQ: + return "QueryLOD"; + case TGSI_OPCODE_TXP: + if (inst->Texture.Texture == TGSI_TEXTURE_CUBE || + inst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY || + inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY) + return ""; + else if (inst->Texture.NumOffsets == 1) + return "ProjOffset"; + else + return "Proj"; + case TGSI_OPCODE_TXL: + case TGSI_OPCODE_TXL2: + if (inst->Texture.NumOffsets == 1) + return "LodOffset"; + else + return "Lod"; + case TGSI_OPCODE_TXD: + if (inst->Texture.NumOffsets == 1) + return "GradOffset"; + else + return "Grad"; + case TGSI_OPCODE_TG4: + if (inst->Texture.NumOffsets == 4) + return "GatherOffsets"; + else if (inst->Texture.NumOffsets == 1) + return "GatherOffset"; + else + return "Gather"; + default: + if (inst->Texture.NumOffsets == 1) + return "Offset"; + else + return ""; + } +} + +static bool fill_offset_buffer(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + char *offbuf) +{ + if (inst->TexOffsets[0].File == TGSI_FILE_IMMEDIATE) { + struct immed *imd = &ctx->imm[inst->TexOffsets[0].Index]; + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + snprintf(offbuf, 256, ", ivec2(%d, 0)", imd->val[inst->TexOffsets[0].SwizzleX].i); + break; + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + snprintf(offbuf, 256, ", ivec2(%d, %d)", imd->val[inst->TexOffsets[0].SwizzleX].i, imd->val[inst->TexOffsets[0].SwizzleY].i); + break; + case TGSI_TEXTURE_3D: + snprintf(offbuf, 256, ", ivec3(%d, %d, %d)", imd->val[inst->TexOffsets[0].SwizzleX].i, imd->val[inst->TexOffsets[0].SwizzleY].i, + imd->val[inst->TexOffsets[0].SwizzleZ].i); + break; + default: + return false; + } + } else if (inst->TexOffsets[0].File == TGSI_FILE_TEMPORARY) { + struct vrend_temp_range *range = find_temp_range(ctx, inst->TexOffsets[0].Index); + int idx = inst->TexOffsets[0].Index - range->first; + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + snprintf(offbuf, 256, ", int(floatBitsToInt(temp%d[%d].%c))", + range->first, idx, + get_swiz_char(inst->TexOffsets[0].SwizzleX)); + break; + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + snprintf(offbuf, 256, ", ivec2(floatBitsToInt(temp%d[%d].%c), floatBitsToInt(temp%d[%d].%c))", + range->first, idx, + get_swiz_char(inst->TexOffsets[0].SwizzleX), + range->first, idx, + get_swiz_char(inst->TexOffsets[0].SwizzleY)); + break; + case TGSI_TEXTURE_3D: + snprintf(offbuf, 256, ", ivec3(floatBitsToInt(temp%d[%d].%c), floatBitsToInt(temp%d[%d].%c), floatBitsToInt(temp%d[%d].%c)", + range->first, idx, + get_swiz_char(inst->TexOffsets[0].SwizzleX), + range->first, idx, + get_swiz_char(inst->TexOffsets[0].SwizzleY), + range->first, idx, + get_swiz_char(inst->TexOffsets[0].SwizzleZ)); + break; + default: + return false; + break; + } + } else if (inst->TexOffsets[0].File == TGSI_FILE_INPUT) { + for (uint32_t j = 0; j < ctx->num_inputs; j++) { + if (ctx->inputs[j].first != inst->TexOffsets[0].Index) + continue; + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + snprintf(offbuf, 256, ", int(floatBitsToInt(%s.%c))", + ctx->inputs[j].glsl_name, + get_swiz_char(inst->TexOffsets[0].SwizzleX)); + break; + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + snprintf(offbuf, 256, ", ivec2(floatBitsToInt(%s.%c), floatBitsToInt(%s.%c))", + ctx->inputs[j].glsl_name, + get_swiz_char(inst->TexOffsets[0].SwizzleX), + ctx->inputs[j].glsl_name, + get_swiz_char(inst->TexOffsets[0].SwizzleY)); + break; + case TGSI_TEXTURE_3D: + snprintf(offbuf, 256, ", ivec3(floatBitsToInt(%s.%c), floatBitsToInt(%s.%c), floatBitsToInt(%s.%c)", + ctx->inputs[j].glsl_name, + get_swiz_char(inst->TexOffsets[0].SwizzleX), + ctx->inputs[j].glsl_name, + get_swiz_char(inst->TexOffsets[0].SwizzleY), + ctx->inputs[j].glsl_name, + get_swiz_char(inst->TexOffsets[0].SwizzleZ)); + break; + default: + return false; + break; + } + } + } + return true; +} + +static void translate_tex(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + struct source_info *sinfo, + struct dest_info *dinfo, + const char *srcs[4], + const char *dst, + const char *writemask) +{ + enum vrend_type_qualifier txfi = TYPE_CONVERSION_NONE; + unsigned twm = TGSI_WRITEMASK_NONE, gwm = TGSI_WRITEMASK_NONE; + enum vrend_type_qualifier dtypeprefix = TYPE_CONVERSION_NONE; + bool is_shad; + char offbuf[256] = ""; + char bias[256] = ""; + int sampler_index; + const char *tex_ext; + + set_texture_reqs(ctx, inst, sinfo->sreg_index); + is_shad = samplertype_is_shadow(inst->Texture.Texture); + + switch (ctx->samplers[sinfo->sreg_index].tgsi_sampler_return) { + case TGSI_RETURN_TYPE_SINT: + /* if dstconv isn't an int */ + if (dinfo->dstconv != INT) + dtypeprefix = INT_BITS_TO_FLOAT; + break; + case TGSI_RETURN_TYPE_UINT: + /* if dstconv isn't an int */ + if (dinfo->dstconv != INT) + dtypeprefix = UINT_BITS_TO_FLOAT; + break; + } + + sampler_index = 1; + + if (inst->Instruction.Opcode == TGSI_OPCODE_LODQ) + ctx->shader_req_bits |= SHADER_REQ_LODQ; + + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_BUFFER: + if (inst->Instruction.Opcode == TGSI_OPCODE_TXP) + twm = TGSI_WRITEMASK_NONE; + else + twm = TGSI_WRITEMASK_X; + txfi = INT; + break; + case TGSI_TEXTURE_1D_ARRAY: + twm = TGSI_WRITEMASK_XY; + txfi = IVEC2; + break; + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_RECT: + if (inst->Instruction.Opcode == TGSI_OPCODE_TXP) + twm = TGSI_WRITEMASK_NONE; + else + twm = TGSI_WRITEMASK_XY; + txfi = IVEC2; + break; + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + case TGSI_TEXTURE_SHADOWRECT: + case TGSI_TEXTURE_3D: + if (inst->Instruction.Opcode == TGSI_OPCODE_TXP) + twm = TGSI_WRITEMASK_NONE; + else if (inst->Instruction.Opcode == TGSI_OPCODE_TG4) + twm = TGSI_WRITEMASK_XY; + else + twm = TGSI_WRITEMASK_XYZ; + txfi = IVEC3; + break; + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_2D_ARRAY: + twm = TGSI_WRITEMASK_XYZ; + txfi = IVEC3; + break; + case TGSI_TEXTURE_2D_MSAA: + twm = TGSI_WRITEMASK_XY; + txfi = IVEC2; + break; + case TGSI_TEXTURE_2D_ARRAY_MSAA: + twm = TGSI_WRITEMASK_XYZ; + txfi = IVEC3; + break; + + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + default: + if (inst->Instruction.Opcode == TGSI_OPCODE_TG4 && + inst->Texture.Texture != TGSI_TEXTURE_CUBE_ARRAY && + inst->Texture.Texture != TGSI_TEXTURE_SHADOWCUBE_ARRAY) + twm = TGSI_WRITEMASK_XYZ; + else + twm = TGSI_WRITEMASK_NONE; + txfi = TYPE_CONVERSION_NONE; + break; + } + + if (inst->Instruction.Opcode == TGSI_OPCODE_TXD) { + switch (inst->Texture.Texture) { + case TGSI_TEXTURE_1D: + case TGSI_TEXTURE_SHADOW1D: + case TGSI_TEXTURE_1D_ARRAY: + case TGSI_TEXTURE_SHADOW1D_ARRAY: + gwm = TGSI_WRITEMASK_X; + break; + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_SHADOW2D: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_SHADOW2D_ARRAY: + case TGSI_TEXTURE_RECT: + case TGSI_TEXTURE_SHADOWRECT: + gwm = TGSI_WRITEMASK_XY; + break; + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_SHADOWCUBE: + case TGSI_TEXTURE_CUBE_ARRAY: + gwm = TGSI_WRITEMASK_XYZ; + break; + default: + gwm = TGSI_WRITEMASK_NONE; + break; + } + } + + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_TXB2: + case TGSI_OPCODE_TXL2: + case TGSI_OPCODE_TEX2: + sampler_index = 2; + if (inst->Instruction.Opcode != TGSI_OPCODE_TEX2) + snprintf(bias, 64, ", %s.x", srcs[1]); + else if (inst->Texture.Texture == TGSI_TEXTURE_SHADOWCUBE_ARRAY) + snprintf(bias, 64, ", float(%s)", srcs[1]); + break; + case TGSI_OPCODE_TXB: + case TGSI_OPCODE_TXL: + snprintf(bias, 64, ", %s.w", srcs[0]); + break; + case TGSI_OPCODE_TXF: + if (inst->Texture.Texture == TGSI_TEXTURE_1D || + inst->Texture.Texture == TGSI_TEXTURE_2D || + inst->Texture.Texture == TGSI_TEXTURE_2D_MSAA || + inst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY_MSAA || + inst->Texture.Texture == TGSI_TEXTURE_3D || + inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY || + inst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY) + snprintf(bias, 64, ", int(%s.w)", srcs[0]); + break; + case TGSI_OPCODE_TXD: + if ((inst->Texture.Texture == TGSI_TEXTURE_1D || + inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D || + inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY || + inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D_ARRAY)) + snprintf(bias, 128, ", vec2(%s%s, 0), vec2(%s%s, 0)", srcs[1], get_wm_string(gwm), srcs[2], get_wm_string(gwm)); + else + snprintf(bias, 128, ", %s%s, %s%s", srcs[1], get_wm_string(gwm), srcs[2], get_wm_string(gwm)); + sampler_index = 3; + break; + case TGSI_OPCODE_TG4: + sampler_index = 2; + ctx->shader_req_bits |= SHADER_REQ_TG4; + if (inst->Texture.NumOffsets == 1) { + if (inst->TexOffsets[0].File != TGSI_FILE_IMMEDIATE) + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + } + if (is_shad) { + if (inst->Texture.Texture == TGSI_TEXTURE_SHADOWCUBE || + inst->Texture.Texture == TGSI_TEXTURE_SHADOW2D_ARRAY) + snprintf(bias, 64, ", %s.w", srcs[0]); + else if (inst->Texture.Texture == TGSI_TEXTURE_SHADOWCUBE_ARRAY) + snprintf(bias, 64, ", %s.x", srcs[1]); + else + snprintf(bias, 64, ", %s.z", srcs[0]); + } else if (sinfo->tg4_has_component) { + if (inst->Texture.NumOffsets == 0) { + if (inst->Texture.Texture == TGSI_TEXTURE_2D || + inst->Texture.Texture == TGSI_TEXTURE_RECT || + inst->Texture.Texture == TGSI_TEXTURE_CUBE || + inst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY || + inst->Texture.Texture == TGSI_TEXTURE_CUBE_ARRAY) + snprintf(bias, 64, ", int(%s)", srcs[1]); + } else if (inst->Texture.NumOffsets) { + if (inst->Texture.Texture == TGSI_TEXTURE_2D || + inst->Texture.Texture == TGSI_TEXTURE_RECT || + inst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY) + snprintf(bias, 64, ", int(%s)", srcs[1]); + } + } + break; + default: + bias[0] = 0; + } + + tex_ext = get_tex_inst_ext(inst); + + if (inst->Texture.NumOffsets == 1) { + if (inst->TexOffsets[0].Index >= (int)ARRAY_SIZE(ctx->imm)) { + set_buf_error(ctx); + return; + } + + if (!fill_offset_buffer(ctx, inst, offbuf)) { + set_buf_error(ctx); + return; + } + + if (inst->Instruction.Opcode == TGSI_OPCODE_TXL || inst->Instruction.Opcode == TGSI_OPCODE_TXL2 || inst->Instruction.Opcode == TGSI_OPCODE_TXD || (inst->Instruction.Opcode == TGSI_OPCODE_TG4 && is_shad)) { + char tmp[256]; + strcpy(tmp, offbuf); + strcpy(offbuf, bias); + strcpy(bias, tmp); + } + } + + /* On GLES we have to normalized the coordinate for all but the texel fetch instruction */ + if (inst->Instruction.Opcode != TGSI_OPCODE_TXF && + (inst->Texture.Texture == TGSI_TEXTURE_RECT || + inst->Texture.Texture == TGSI_TEXTURE_SHADOWRECT)) { + + char buf[255]; + const char *new_srcs[4] = { buf, srcs[1], srcs[2], srcs[3] }; + + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_TXP: + snprintf(buf, 255, "vec4(%s)/vec4(textureSize(%s, 0), 1, 1)", srcs[0], srcs[sampler_index]); + break; + + case TGSI_OPCODE_TG4: + snprintf(buf, 255, "%s.xy/vec2(textureSize(%s, 0))", srcs[0], srcs[sampler_index]); + break; + + default: + /* Non TG4 ops have the compare value in the z components */ + if (inst->Texture.Texture == TGSI_TEXTURE_SHADOWRECT) { + snprintf(buf, 255, "vec3(%s.xy/vec2(textureSize(%s, 0)), %s.z)", srcs[0], srcs[sampler_index], srcs[0]); + } else + snprintf(buf, 255, "%s.xy/vec2(textureSize(%s, 0))", srcs[0], srcs[sampler_index]); + } + srcs = new_srcs; + } + + if (inst->Instruction.Opcode == TGSI_OPCODE_TXF) { + if ((inst->Texture.Texture == TGSI_TEXTURE_1D || + inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY || + inst->Texture.Texture == TGSI_TEXTURE_RECT)) { + if (inst->Texture.Texture == TGSI_TEXTURE_1D) + emit_buff(ctx, "%s = %s(%s(texelFetch%s(%s, ivec2(%s(%s%s), 0)%s%s)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], get_string(txfi), srcs[0], + get_wm_string(twm), bias, offbuf, + dinfo->dst_override_no_wm[0] ? "" : writemask); + else if (inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY) { + /* the y coordinate must go into the z element and the y must be zero */ + emit_buff(ctx, "%s = %s(%s(texelFetch%s(%s, ivec3(%s(%s%s), 0).xzy%s%s)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], get_string(txfi), srcs[0], + get_wm_string(twm), bias, offbuf, + dinfo->dst_override_no_wm[0] ? "" : writemask); + } else { + emit_buff(ctx, "%s = %s(%s(texelFetch%s(%s, %s(%s%s), 0%s)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], get_string(txfi), srcs[0], + get_wm_string(twm), offbuf, + dinfo->dst_override_no_wm[0] ? "" : writemask); + } + } else { + emit_buff(ctx, "%s = %s(%s(texelFetch%s(%s, %s(%s%s)%s%s)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], get_string(txfi), srcs[0], + get_wm_string(twm), bias, offbuf, + dinfo->dst_override_no_wm[0] ? "" : writemask); + } + } else if (ctx->cfg->glsl_version < 140 && (ctx->shader_req_bits & SHADER_REQ_SAMPLER_RECT)) { + /* rect is special in GLSL 1.30 */ + if (inst->Texture.Texture == TGSI_TEXTURE_RECT) + emit_buff(ctx, "%s = texture2DRect(%s, %s.xy)%s;\n", + dst, srcs[sampler_index], srcs[0], writemask); + else if (inst->Texture.Texture == TGSI_TEXTURE_SHADOWRECT) + emit_buff(ctx, "%s = shadow2DRect(%s, %s.xyz)%s;\n", + dst, srcs[sampler_index], srcs[0], writemask); + } else if (is_shad && inst->Instruction.Opcode != TGSI_OPCODE_TG4) { /* TGSI returns 1.0 in alpha */ + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + const struct tgsi_full_src_register *src = &inst->Src[sampler_index]; + + if ((inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D || + inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D_ARRAY)) { + if (inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D) { + if (inst->Instruction.Opcode == TGSI_OPCODE_TXP) + emit_buff(ctx, "%s = %s(%s(vec4(vec4(texture%s(%s, vec4(%s%s.xzw, 0).xwyz %s%s)) * %sshadmask%d + %sshadadd%d)%s));\n", + dst, get_string(dinfo->dstconv), + get_string(dtypeprefix), tex_ext, srcs[sampler_index], + srcs[0], get_wm_string(twm), offbuf, bias, cname, + src->Register.Index, cname, + src->Register.Index, writemask); + else + emit_buff(ctx, "%s = %s(%s(vec4(vec4(texture%s(%s, vec3(%s%s.xz, 0).xzy %s%s)) * %sshadmask%d + %sshadadd%d)%s));\n", + dst, get_string(dinfo->dstconv), + get_string(dtypeprefix), tex_ext, srcs[sampler_index], + srcs[0], get_wm_string(twm), offbuf, bias, cname, + src->Register.Index, cname, + src->Register.Index, writemask); + } else if (inst->Texture.Texture == TGSI_TEXTURE_SHADOW1D_ARRAY) { + emit_buff(ctx, "%s = %s(%s(vec4(vec4(texture%s(%s, vec4(%s%s, 0).xwyz %s%s)) * %sshadmask%d + %sshadadd%d)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], srcs[0], + get_wm_string(twm), offbuf, bias, cname, + src->Register.Index, cname, + src->Register.Index, writemask); + } + } else + emit_buff(ctx, "%s = %s(%s(vec4(vec4(texture%s(%s, %s%s%s%s)) * %sshadmask%d + %sshadadd%d)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], srcs[0], + get_wm_string(twm), offbuf, bias, cname, + src->Register.Index, cname, + src->Register.Index, writemask); + } else { + /* OpenGL ES do not support 1D texture + * so we use a 2D texture with a parameter set to 0.5 + */ + if ((inst->Texture.Texture == TGSI_TEXTURE_1D || + inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY)) { + if (inst->Texture.Texture == TGSI_TEXTURE_1D) { + if (inst->Instruction.Opcode == TGSI_OPCODE_TXP) + emit_buff(ctx, "%s = %s(%s(texture%s(%s, vec3(%s.xw, 0).xzy %s%s)%s));\n", + dst, get_string(dinfo->dstconv), + get_string(dtypeprefix), tex_ext, srcs[sampler_index], + srcs[0], offbuf, bias, + dinfo->dst_override_no_wm[0] ? "" : writemask); + else + emit_buff(ctx, "%s = %s(%s(texture%s(%s, vec2(%s%s, 0.5) %s%s)%s));\n", + dst, get_string(dinfo->dstconv), + get_string(dtypeprefix), tex_ext, srcs[sampler_index], + srcs[0], get_wm_string(twm), offbuf, bias, + dinfo->dst_override_no_wm[0] ? "" : writemask); + } else if (inst->Texture.Texture == TGSI_TEXTURE_1D_ARRAY) { + if (inst->Instruction.Opcode == TGSI_OPCODE_TXP) + emit_buff(ctx, "%s = %s(%s(texture%s(%s, vec3(%s.x / %s.w, 0, %s.y) %s%s)%s));\n", + dst, get_string(dinfo->dstconv), + get_string(dtypeprefix), tex_ext, srcs[sampler_index], + srcs[0], srcs[0], srcs[0], offbuf, bias, + dinfo->dst_override_no_wm[0] ? "" : writemask); + else + emit_buff(ctx, "%s = %s(%s(texture%s(%s, vec3(%s%s, 0).xzy %s%s)%s));\n", + dst, get_string(dinfo->dstconv), + get_string(dtypeprefix), tex_ext, srcs[sampler_index], + srcs[0], get_wm_string(twm), offbuf, bias, + dinfo->dst_override_no_wm[0] ? "" : writemask); + } + } else { + emit_buff(ctx, "%s = %s(%s(texture%s(%s, %s%s%s%s)%s));\n", + dst, get_string(dinfo->dstconv), get_string(dtypeprefix), + tex_ext, srcs[sampler_index], srcs[0], get_wm_string(twm), + offbuf, bias, dinfo->dst_override_no_wm[0] ? "" : writemask); + } + } +} + +static void +create_swizzled_clipdist(struct dump_ctx *ctx, + struct vrend_strbuf *result, + const struct tgsi_full_src_register *src, + int input_idx, + bool gl_in, + const char *stypeprefix, + const char *prefix, + const char *arrayname, int offset) +{ + char clipdistvec[4][64] = { 0, }; + + char clip_indirect[32] = ""; + + bool has_prev_vals = (ctx->key->prev_stage_num_cull_out + ctx->key->prev_stage_num_clip_out) > 0; + int num_culls = has_prev_vals ? ctx->key->prev_stage_num_cull_out : 0; + int num_clips = has_prev_vals ? ctx->key->prev_stage_num_clip_out : ctx->num_in_clip_dist; + int base_idx = ctx->inputs[input_idx].sid * 4; + + /* With arrays enabled , but only when gl_ClipDistance or gl_CullDistance are emitted (>4) + * then we need to add indirect addressing */ + if (src->Register.Indirect && ((num_clips > 4 && base_idx < num_clips) || num_culls > 4)) + snprintf(clip_indirect, 32, "4*addr%d +", src->Indirect.Index); + else if (src->Register.Index != offset) + snprintf(clip_indirect, 32, "4*%d +", src->Register.Index - offset); + + for (unsigned cc = 0; cc < 4; cc++) { + const char *cc_name = ctx->inputs[input_idx].glsl_name; + int idx = base_idx; + if (cc == 0) + idx += src->Register.SwizzleX; + else if (cc == 1) + idx += src->Register.SwizzleY; + else if (cc == 2) + idx += src->Register.SwizzleZ; + else if (cc == 3) + idx += src->Register.SwizzleW; + + if (num_culls) { + if (idx >= num_clips) { + idx -= num_clips; + cc_name = "gl_CullDistance"; + } + if (ctx->key->prev_stage_num_cull_out) + if (idx >= ctx->key->prev_stage_num_cull_out) + idx = 0; + } else { + if (ctx->key->prev_stage_num_clip_out) + if (idx >= ctx->key->prev_stage_num_clip_out) + idx = 0; + } + if (gl_in) + snprintf(clipdistvec[cc], 64, "%sgl_in%s.%s[%s %d]", prefix, arrayname, cc_name, clip_indirect, idx); + else + snprintf(clipdistvec[cc], 64, "%s%s%s[%s %d]", prefix, arrayname, cc_name, clip_indirect, idx); + } + strbuf_fmt(result, "%s(vec4(%s,%s,%s,%s))", stypeprefix, clipdistvec[0], clipdistvec[1], clipdistvec[2], clipdistvec[3]); +} + +static +void load_clipdist_fs(struct dump_ctx *ctx, + struct vrend_strbuf *result, + const struct tgsi_full_src_register *src, + int input_idx, + bool gl_in, + const char *stypeprefix, + int offset) +{ + char clip_indirect[32] = ""; + + int base_idx = ctx->inputs[input_idx].sid; + + /* With arrays enabled , but only when gl_ClipDistance or gl_CullDistance are emitted (>4) + * then we need to add indirect addressing */ + if (src->Register.Indirect) + snprintf(clip_indirect, 32, "addr%d + %d", src->Indirect.Index, base_idx); + else + snprintf(clip_indirect, 32, "%d + %d", src->Register.Index - offset, base_idx); + + if (gl_in) + strbuf_fmt(result, "%s(clip_dist_temp[%s])", stypeprefix, clip_indirect); + else + strbuf_fmt(result, "%s(clip_dist_temp[%s])", stypeprefix, clip_indirect); +} + + +static enum vrend_type_qualifier get_coord_prefix(int resource, bool *is_ms) +{ + switch(resource) { + case TGSI_TEXTURE_1D: + return IVEC2; + case TGSI_TEXTURE_BUFFER: + return INT; + case TGSI_TEXTURE_1D_ARRAY: + return IVEC3; + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_RECT: + return IVEC2; + case TGSI_TEXTURE_3D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_2D_ARRAY: + case TGSI_TEXTURE_CUBE_ARRAY: + return IVEC3; + case TGSI_TEXTURE_2D_MSAA: + *is_ms = true; + return IVEC2; + case TGSI_TEXTURE_2D_ARRAY_MSAA: + *is_ms = true; + return IVEC3; + default: + return TYPE_CONVERSION_NONE; + } +} + +static bool is_integer_memory(struct dump_ctx *ctx, enum tgsi_file_type file_type, uint32_t index) +{ + switch(file_type) { + case TGSI_FILE_BUFFER: + return !!(ctx->ssbo_integer_mask & (1 << index)); + case TGSI_FILE_MEMORY: + return ctx->integer_memory; + default: + return false; + } +} + +static void set_memory_qualifier(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + uint32_t reg_index, bool indirect) +{ + if (inst->Memory.Qualifier == TGSI_MEMORY_COHERENT) { + if (indirect) { + uint32_t mask = ctx->ssbo_used_mask; + while (mask) + ctx->ssbo_memory_qualifier[u_bit_scan(&mask)] = TGSI_MEMORY_COHERENT; + } else + ctx->ssbo_memory_qualifier[reg_index] = TGSI_MEMORY_COHERENT; + + } +} + +static void emit_store_mem(struct dump_ctx *ctx, const char *dst, int writemask, + const char *srcs[4], const char *conversion) +{ + static const char swizzle_char[] = "xyzw"; + for (int i = 0; i < 4; ++i) { + if (writemask & (1 << i)) { + emit_buff(ctx, "%s[(uint(floatBitsToUint(%s)) >> 2) + %du] = %s(%s).%c;\n", + dst, srcs[0], i, conversion, srcs[1], swizzle_char[i]); + } + } +} + +static void +translate_store(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + struct source_info *sinfo, + const char *srcs[4], + const char *dst) +{ + const struct tgsi_full_dst_register *dst_reg = &inst->Dst[0]; + + if (dst_reg->Register.File == TGSI_FILE_IMAGE) { + bool is_ms = false; + enum vrend_type_qualifier coord_prefix = get_coord_prefix(ctx->images[dst_reg->Register.Index].decl.Resource, &is_ms); + enum tgsi_return_type itype; + char ms_str[32] = ""; + enum vrend_type_qualifier stypeprefix = TYPE_CONVERSION_NONE; + const char *conversion = sinfo->override_no_cast[0] ? "" : get_string(FLOAT_BITS_TO_INT); + get_internalformat_string(inst->Memory.Format, &itype); + if (is_ms) { + snprintf(ms_str, 32, "int(%s.w),", srcs[0]); + } + switch (itype) { + case TGSI_RETURN_TYPE_UINT: + stypeprefix = FLOAT_BITS_TO_UINT; + break; + case TGSI_RETURN_TYPE_SINT: + stypeprefix = FLOAT_BITS_TO_INT; + break; + default: + break; + } + if (!dst_reg->Register.Indirect) { + emit_buff(ctx, "imageStore(%s,%s(%s(%s)),%s%s(%s));\n", + dst, get_string(coord_prefix), conversion, srcs[0], + ms_str, get_string(stypeprefix), srcs[1]); + } else { + struct vrend_array *image = lookup_image_array_ptr(ctx, dst_reg->Register.Index); + if (image) { + int basearrayidx = image->first; + int array_size = image->array_size; + emit_buff(ctx, "switch (addr%d + %d) {\n", dst_reg->Indirect.Index, + dst_reg->Register.Index - basearrayidx); + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + + for (int i = 0; i < array_size; ++i) { + emit_buff(ctx, "case %d: imageStore(%simg%d[%d],%s(%s(%s)),%s%s(%s)); break;\n", + i, cname, basearrayidx, i, get_string(coord_prefix), + conversion, srcs[0], ms_str, get_string(stypeprefix), + srcs[1]); + } + emit_buff(ctx, "}\n"); + } + } + } else if (dst_reg->Register.File == TGSI_FILE_BUFFER || + dst_reg->Register.File == TGSI_FILE_MEMORY) { + enum vrend_type_qualifier dtypeprefix; + set_memory_qualifier(ctx, inst, dst_reg->Register.Index, + dst_reg->Register.Indirect); + dtypeprefix = is_integer_memory(ctx, dst_reg->Register.File, dst_reg->Register.Index) ? + FLOAT_BITS_TO_INT : FLOAT_BITS_TO_UINT; + const char *conversion = sinfo->override_no_cast[1] ? "" : get_string(dtypeprefix); + + if (!dst_reg->Register.Indirect) { + emit_store_mem(ctx, dst, dst_reg->Register.WriteMask, srcs, + conversion); + } else { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + bool atomic_ssbo = ctx->ssbo_atomic_mask & (1 << dst_reg->Register.Index); + int base = atomic_ssbo ? ctx->ssbo_atomic_array_base : ctx->ssbo_array_base; + uint32_t mask = ctx->ssbo_used_mask; + int start, array_count; + u_bit_scan_consecutive_range(&mask, &start, &array_count); + int basearrayidx = lookup_image_array(ctx, dst_reg->Register.Index); + emit_buff(ctx, "switch (addr%d + %d) {\n", dst_reg->Indirect.Index, + dst_reg->Register.Index - base); + + for (int i = 0; i < array_count; ++i) { + char dst_tmp[128]; + emit_buff(ctx, "case %d:\n", i); + snprintf(dst_tmp, 128, "%simg%d[%d]", cname, basearrayidx, i); + emit_store_mem(ctx, dst_tmp, dst_reg->Register.WriteMask, srcs, + conversion); + emit_buff(ctx, "break;\n"); + } + emit_buf(ctx, "}\n"); + } + } +} + +static void emit_load_mem(struct dump_ctx *ctx, const char *dst, int writemask, + const char *conversion, const char *atomic_op, const char *src0, + const char *atomic_src) +{ + static const char swizzle_char[] = "xyzw"; + for (int i = 0; i < 4; ++i) { + if (writemask & (1 << i)) { + emit_buff(ctx, "%s.%c = (%s(%s(%s[ssbo_addr_temp + %du]%s)));\n", dst, + swizzle_char[i], conversion, atomic_op, src0, i, atomic_src); + } + } +} + + +static void +translate_load(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + struct source_info *sinfo, + struct dest_info *dinfo, + const char *srcs[4], + const char *dst, + const char *writemask) +{ + const struct tgsi_full_src_register *src = &inst->Src[0]; + if (src->Register.File == TGSI_FILE_IMAGE) { + bool is_ms = false; + enum vrend_type_qualifier coord_prefix = get_coord_prefix(ctx->images[sinfo->sreg_index].decl.Resource, &is_ms); + enum vrend_type_qualifier dtypeprefix = TYPE_CONVERSION_NONE; + const char *conversion = sinfo->override_no_cast[1] ? "" : get_string(FLOAT_BITS_TO_INT); + enum tgsi_return_type itype; + get_internalformat_string(ctx->images[sinfo->sreg_index].decl.Format, &itype); + char ms_str[32] = ""; + const char *wm = dinfo->dst_override_no_wm[0] ? "" : writemask; + if (is_ms) { + snprintf(ms_str, 32, ", int(%s.w)", srcs[1]); + } + switch (itype) { + case TGSI_RETURN_TYPE_UINT: + dtypeprefix = UINT_BITS_TO_FLOAT; + break; + case TGSI_RETURN_TYPE_SINT: + dtypeprefix = INT_BITS_TO_FLOAT; + break; + default: + break; + } + + /* On GL WR translates to writable, but on GLES we translate this to writeonly + * because for most formats one has to specify one or the other, so if we have an + * image with the TGSI WR specification, and read from it, we drop the Writable flag. + * For the images that allow RW this is of no consequence, and for the others a write + * access will fail instead of the read access, but this doesn't constitue a regression + * because we couldn't do both, read and write, anyway. */ + if (ctx->images[sinfo->sreg_index].decl.Writable && + (ctx->images[sinfo->sreg_index].decl.Format != PIPE_FORMAT_R32_FLOAT) && + (ctx->images[sinfo->sreg_index].decl.Format != PIPE_FORMAT_R32_SINT) && + (ctx->images[sinfo->sreg_index].decl.Format != PIPE_FORMAT_R32_UINT)) + ctx->images[sinfo->sreg_index].decl.Writable = 0; + + if (!inst->Src[0].Register.Indirect) { + emit_buff(ctx, "%s = %s(imageLoad(%s, %s(%s(%s))%s)%s);\n", + dst, get_string(dtypeprefix), + srcs[0], get_string(coord_prefix), conversion, srcs[1], + ms_str, wm); + } else { + char src[32] = ""; + struct vrend_array *image = lookup_image_array_ptr(ctx, inst->Src[0].Register.Index); + if (image) { + int basearrayidx = image->first; + int array_size = image->array_size; + emit_buff(ctx, "switch (addr%d + %d) {\n", inst->Src[0].Indirect.Index, inst->Src[0].Register.Index - basearrayidx); + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + + for (int i = 0; i < array_size; ++i) { + snprintf(src, 32, "%simg%d[%d]", cname, basearrayidx, i); + emit_buff(ctx, "case %d: %s = %s(imageLoad(%s, %s(%s(%s))%s)%s);break;\n", + i, dst, get_string(dtypeprefix), + src, get_string(coord_prefix), conversion, srcs[1], + ms_str, wm); + } + emit_buff(ctx, "}\n"); + } + } + } else if (src->Register.File == TGSI_FILE_BUFFER || + src->Register.File == TGSI_FILE_MEMORY) { + char mydst[255], atomic_op[9], atomic_src[10]; + enum vrend_type_qualifier dtypeprefix; + + set_memory_qualifier(ctx, inst, inst->Src[0].Register.Index, inst->Src[0].Register.Indirect); + + strcpy(mydst, dst); + char *wmp = strchr(mydst, '.'); + + if (wmp) + wmp[0] = 0; + emit_buff(ctx, "ssbo_addr_temp = uint(floatBitsToUint(%s)) >> 2;\n", srcs[1]); + + atomic_op[0] = atomic_src[0] = '\0'; + if (ctx->ssbo_atomic_mask & (1 << src->Register.Index)) { + /* Emulate atomicCounter with atomicOr. */ + strcpy(atomic_op, "atomicOr"); + strcpy(atomic_src, ", uint(0)"); + } + + dtypeprefix = (is_integer_memory(ctx, src->Register.File, src->Register.Index)) ? INT_BITS_TO_FLOAT : UINT_BITS_TO_FLOAT; + + if (!inst->Src[0].Register.Indirect) { + emit_load_mem(ctx, mydst, inst->Dst[0].Register.WriteMask, get_string(dtypeprefix), atomic_op, srcs[0], atomic_src); + } else { + char src[128] = ""; + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + bool atomic_ssbo = ctx->ssbo_atomic_mask & (1 << inst->Src[0].Register.Index); + const char *atomic_str = atomic_ssbo ? "atomic" : ""; + uint base = atomic_ssbo ? ctx->ssbo_atomic_array_base : ctx->ssbo_array_base; + int start, array_count; + uint32_t mask = ctx->ssbo_used_mask; + u_bit_scan_consecutive_range(&mask, &start, &array_count); + + emit_buff(ctx, "switch (addr%d + %d) {\n", inst->Src[0].Indirect.Index, inst->Src[0].Register.Index - base); + for (int i = 0; i < array_count; ++i) { + emit_buff(ctx, "case %d:\n", i); + snprintf(src, 128,"%sssboarr%s[%d].%sssbocontents%d", cname, atomic_str, i, cname, base); + emit_load_mem(ctx, mydst, inst->Dst[0].Register.WriteMask, get_string(dtypeprefix), atomic_op, src, atomic_src); + emit_buff(ctx, " break;\n"); + } + emit_buf(ctx, "}\n"); + } + } else if (src->Register.File == TGSI_FILE_HW_ATOMIC) { + emit_buff(ctx, "%s = uintBitsToFloat(atomicCounter(%s));\n", dst, srcs[0]); + } +} + +static const char *get_atomic_opname(int tgsi_opcode, bool *is_cas) +{ + const char *opname; + *is_cas = false; + switch (tgsi_opcode) { + case TGSI_OPCODE_ATOMUADD: + opname = "Add"; + break; + case TGSI_OPCODE_ATOMXCHG: + opname = "Exchange"; + break; + case TGSI_OPCODE_ATOMCAS: + opname = "CompSwap"; + *is_cas = true; + break; + case TGSI_OPCODE_ATOMAND: + opname = "And"; + break; + case TGSI_OPCODE_ATOMOR: + opname = "Or"; + break; + case TGSI_OPCODE_ATOMXOR: + opname = "Xor"; + break; + case TGSI_OPCODE_ATOMUMIN: + opname = "Min"; + break; + case TGSI_OPCODE_ATOMUMAX: + opname = "Max"; + break; + case TGSI_OPCODE_ATOMIMIN: + opname = "Min"; + break; + case TGSI_OPCODE_ATOMIMAX: + opname = "Max"; + break; + default: + return NULL; + } + return opname; +} + +static void +translate_resq(struct dump_ctx *ctx, struct tgsi_full_instruction *inst, + const char *srcs[4], const char *dst, const char *writemask) +{ + const struct tgsi_full_src_register *src = &inst->Src[0]; + + if (src->Register.File == TGSI_FILE_IMAGE) { + if (inst->Dst[0].Register.WriteMask & 0x8) { + ctx->shader_req_bits |= SHADER_REQ_TXQS | SHADER_REQ_INTS; + emit_buff(ctx, "%s = %s(imageSamples(%s));\n", + dst, get_string(INT_BITS_TO_FLOAT), srcs[0]); + } + if (inst->Dst[0].Register.WriteMask & 0x7) { + const char *swizzle_mask = inst->Memory.Texture == TGSI_TEXTURE_1D_ARRAY ? + ".xz" : ""; + ctx->shader_req_bits |= SHADER_REQ_IMAGE_SIZE | SHADER_REQ_INTS; + bool skip_emit_writemask = inst->Memory.Texture == TGSI_TEXTURE_BUFFER; + + emit_buff(ctx, "%s = %s(imageSize(%s)%s%s);\n", + dst, get_string(INT_BITS_TO_FLOAT), srcs[0], + swizzle_mask, skip_emit_writemask ? "" : writemask); + } + } else if (src->Register.File == TGSI_FILE_BUFFER) { + emit_buff(ctx, "%s = %s(int(%s.length()) << 2);\n", + dst, get_string(INT_BITS_TO_FLOAT), srcs[0]); + } +} + +static void +translate_atomic(struct dump_ctx *ctx, + struct tgsi_full_instruction *inst, + struct source_info *sinfo, + const char *srcs[4], + char *dst) +{ + const struct tgsi_full_src_register *src = &inst->Src[0]; + const char *opname; + enum vrend_type_qualifier stypeprefix = TYPE_CONVERSION_NONE; + enum vrend_type_qualifier dtypeprefix = TYPE_CONVERSION_NONE; + enum vrend_type_qualifier stypecast = TYPE_CONVERSION_NONE; + bool is_cas; + char cas_str[128] = ""; + + if (src->Register.File == TGSI_FILE_IMAGE) { + enum tgsi_return_type itype; + get_internalformat_string(ctx->images[sinfo->sreg_index].decl.Format, &itype); + switch (itype) { + default: + case TGSI_RETURN_TYPE_UINT: + stypeprefix = FLOAT_BITS_TO_UINT; + dtypeprefix = UINT_BITS_TO_FLOAT; + stypecast = UINT; + break; + case TGSI_RETURN_TYPE_SINT: + stypeprefix = FLOAT_BITS_TO_INT; + dtypeprefix = INT_BITS_TO_FLOAT; + stypecast = INT; + break; + case TGSI_RETURN_TYPE_FLOAT: + if (ctx->cfg->has_es31_compat) + ctx->shader_req_bits |= SHADER_REQ_ES31_COMPAT; + else + ctx->shader_req_bits |= SHADER_REQ_SHADER_ATOMIC_FLOAT; + stypecast = FLOAT; + break; + } + } else { + stypeprefix = FLOAT_BITS_TO_UINT; + dtypeprefix = UINT_BITS_TO_FLOAT; + stypecast = UINT; + } + + opname = get_atomic_opname(inst->Instruction.Opcode, &is_cas); + if (!opname) { + set_buf_error(ctx); + return; + } + + if (is_cas) + snprintf(cas_str, 128, ", %s(%s(%s))", get_string(stypecast), get_string(stypeprefix), srcs[3]); + + if (src->Register.File == TGSI_FILE_IMAGE) { + bool is_ms = false; + enum vrend_type_qualifier coord_prefix = get_coord_prefix(ctx->images[sinfo->sreg_index].decl.Resource, &is_ms); + const char *conversion = sinfo->override_no_cast[1] ? "" : get_string(FLOAT_BITS_TO_INT); + char ms_str[32] = ""; + if (is_ms) { + snprintf(ms_str, 32, ", int(%s.w)", srcs[1]); + } + + if (!inst->Src[0].Register.Indirect) { + emit_buff(ctx, "%s = %s(imageAtomic%s(%s, %s(%s(%s))%s, %s(%s(%s))%s));\n", + dst, get_string(dtypeprefix), opname, srcs[0], + get_string(coord_prefix), conversion, srcs[1], ms_str, + get_string(stypecast), get_string(stypeprefix), srcs[2], + cas_str); + } else { + char src[32] = ""; + struct vrend_array *image = lookup_image_array_ptr(ctx, inst->Src[0].Register.Index); + if (image) { + int basearrayidx = image->first; + int array_size = image->array_size; + emit_buff(ctx, "switch (addr%d + %d) {\n", inst->Src[0].Indirect.Index, inst->Src[0].Register.Index - basearrayidx); + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + + for (int i = 0; i < array_size; ++i) { + snprintf(src, 32, "%simg%d[%d]", cname, basearrayidx, i); + emit_buff(ctx, "case %d: %s = %s(imageAtomic%s(%s, %s(%s(%s))%s, %s(%s(%s))%s));\n", + i, dst, get_string(dtypeprefix), opname, src, + get_string(coord_prefix), conversion, srcs[1], + ms_str, get_string(stypecast), + get_string(stypeprefix), srcs[2], cas_str); + } + emit_buff(ctx, "}\n"); + } + } + ctx->shader_req_bits |= SHADER_REQ_IMAGE_ATOMIC; + } + if (src->Register.File == TGSI_FILE_BUFFER || src->Register.File == TGSI_FILE_MEMORY) { + enum vrend_type_qualifier type; + if ((is_integer_memory(ctx, src->Register.File, src->Register.Index))) { + type = INT; + dtypeprefix = INT_BITS_TO_FLOAT; + stypeprefix = FLOAT_BITS_TO_INT; + } else { + type = UINT; + dtypeprefix = UINT_BITS_TO_FLOAT; + stypeprefix = FLOAT_BITS_TO_UINT; + } + + emit_buff(ctx, "%s = %s(atomic%s(%s[int(floatBitsToInt(%s)) >> 2], %s(%s(%s).x)%s));\n", + dst, get_string(dtypeprefix), opname, srcs[0], srcs[1], + get_string(type), get_string(stypeprefix), srcs[2], cas_str); + } + if(src->Register.File == TGSI_FILE_HW_ATOMIC) { + if (sinfo->imm_value == -1) + emit_buff(ctx, "%s = %s(atomicCounterDecrement(%s) + 1u);\n", + dst, get_string(dtypeprefix), srcs[0]); + else if (sinfo->imm_value == 1) + emit_buff(ctx, "%s = %s(atomicCounterIncrement(%s));\n", + dst, get_string(dtypeprefix), srcs[0]); + else + emit_buff(ctx, "%s = %s(atomicCounter%sARB(%s, floatBitsToUint(%s).x%s));\n", + dst, get_string(dtypeprefix), opname, srcs[0], srcs[2], + cas_str); + } + +} + +static const char *reswizzle_dest(const struct vrend_shader_io *io, const struct tgsi_full_dst_register *dst_reg, + char *reswizzled, const char *writemask) +{ + if (io->usage_mask != 0xf) { + if (io->num_components > 1) { + int real_wm = dst_reg->Register.WriteMask >> io->swizzle_offset; + int k = 1; + reswizzled[0] = '.'; + for (int i = 0; i < io->num_components; ++i) { + if (real_wm & (1 << i)) + reswizzled[k++] = get_swiz_char(i); + } + reswizzled[k] = 0; + } + writemask = reswizzled; + } + return writemask; +} + +static void get_destination_info_generic(struct dump_ctx *ctx, + const struct tgsi_full_dst_register *dst_reg, + const struct vrend_shader_io *io, + const char *writemask, char dsts[255]) +{ + const char *blkarray = (ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL) ? "[gl_InvocationID]" : ""; + const char *stage_prefix = get_stage_output_name_prefix(ctx->prog_type); + const char *wm = io->override_no_wm ? "" : writemask; + char reswizzled[6] = ""; + + wm = reswizzle_dest(io, dst_reg, reswizzled, writemask); + + if (io->first == io->last) + snprintf(dsts, 255, "%s%s%s", io->glsl_name, blkarray, wm); + else { + if (prefer_generic_io_block(ctx, io_out)) { + char outvarname[64]; + get_blockvarname(outvarname, stage_prefix, io, blkarray); + + if (dst_reg->Register.Indirect) + snprintf(dsts, 255, "%s.%s[addr%d + %d]%s", outvarname, io->glsl_name, + dst_reg->Indirect.Index, dst_reg->Register.Index - io->first, wm); + else + snprintf(dsts, 255, "%s.%s[%d]%s", outvarname, io->glsl_name, + dst_reg->Register.Index - io->first, wm); + } else { + if (dst_reg->Register.Indirect) + snprintf(dsts, 255, "%s%s[addr%d + %d]%s", io->glsl_name, blkarray, + dst_reg->Indirect.Index, dst_reg->Register.Index - io->first, wm); + else + snprintf(dsts, 255, "%s%s[%d]%s", io->glsl_name, blkarray, + dst_reg->Register.Index - io->first, wm); + } + } +} + +static bool +get_destination_info(struct dump_ctx *ctx, + const struct tgsi_full_instruction *inst, + struct dest_info *dinfo, + char dsts[3][255], + char fp64_dsts[3][255], + char *writemask) +{ + const struct tgsi_full_dst_register *dst_reg; + enum tgsi_opcode_type dtype = tgsi_opcode_infer_dst_type(inst->Instruction.Opcode); + + if (dtype == TGSI_TYPE_SIGNED || dtype == TGSI_TYPE_UNSIGNED) + ctx->shader_req_bits |= SHADER_REQ_INTS; + + if (dtype == TGSI_TYPE_DOUBLE) { + /* we need the uvec2 conversion for doubles */ + ctx->shader_req_bits |= SHADER_REQ_INTS | SHADER_REQ_FP64; + } + + if (inst->Instruction.Opcode == TGSI_OPCODE_TXQ) { + dinfo->dtypeprefix = INT_BITS_TO_FLOAT; + } else { + switch (dtype) { + case TGSI_TYPE_UNSIGNED: + dinfo->dtypeprefix = UINT_BITS_TO_FLOAT; + break; + case TGSI_TYPE_SIGNED: + dinfo->dtypeprefix = INT_BITS_TO_FLOAT; + break; + default: + break; + } + } + + for (uint32_t i = 0; i < inst->Instruction.NumDstRegs; i++) { + char fp64_writemask[6] = ""; + dst_reg = &inst->Dst[i]; + dinfo->dst_override_no_wm[i] = false; + if (dst_reg->Register.WriteMask != TGSI_WRITEMASK_XYZW) { + int wm_idx = 0, dbl_wm_idx = 0; + writemask[wm_idx++] = '.'; + fp64_writemask[dbl_wm_idx++] = '.'; + + if (dst_reg->Register.WriteMask & 0x1) + writemask[wm_idx++] = 'x'; + if (dst_reg->Register.WriteMask & 0x2) + writemask[wm_idx++] = 'y'; + if (dst_reg->Register.WriteMask & 0x4) + writemask[wm_idx++] = 'z'; + if (dst_reg->Register.WriteMask & 0x8) + writemask[wm_idx++] = 'w'; + + if (dtype == TGSI_TYPE_DOUBLE) { + if (dst_reg->Register.WriteMask & 0x3) + fp64_writemask[dbl_wm_idx++] = 'x'; + if (dst_reg->Register.WriteMask & 0xc) + fp64_writemask[dbl_wm_idx++] = 'y'; + } + + if (dtype == TGSI_TYPE_DOUBLE) { + if (dbl_wm_idx == 2) + dinfo->dstconv = DOUBLE; + else + dinfo->dstconv = DVEC2; + } else { + dinfo->dstconv = FLOAT + wm_idx - 2; + dinfo->udstconv = UINT + wm_idx - 2; + dinfo->idstconv = INT + wm_idx - 2; + } + } else { + if (dtype == TGSI_TYPE_DOUBLE) + dinfo->dstconv = DVEC2; + else + dinfo->dstconv = VEC4; + dinfo->udstconv = UVEC4; + dinfo->idstconv = IVEC4; + } + + if (dst_reg->Register.File == TGSI_FILE_OUTPUT) { + uint32_t j; + for (j = 0; j < ctx->num_outputs; j++) { + if (ctx->outputs[j].first <= dst_reg->Register.Index && + ctx->outputs[j].last >= dst_reg->Register.Index && + (ctx->outputs[j].usage_mask & dst_reg->Register.WriteMask)) { + if (inst->Instruction.Precise) { + if (!ctx->outputs[j].invariant && ctx->outputs[j].name != TGSI_SEMANTIC_CLIPVERTEX) { + ctx->outputs[j].precise = true; + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + } + } + + if (ctx->glsl_ver_required >= 140 && ctx->outputs[j].name == TGSI_SEMANTIC_CLIPVERTEX) { + snprintf(dsts[i], 255, "clipv_tmp"); + } else if (ctx->outputs[j].name == TGSI_SEMANTIC_CLIPDIST) { + char clip_indirect[32] = ""; + if (ctx->outputs[j].first != ctx->outputs[j].last) { + if (dst_reg->Register.Indirect) + snprintf(clip_indirect, sizeof(clip_indirect), "+ addr%d", dst_reg->Indirect.Index); + else + snprintf(clip_indirect, sizeof(clip_indirect), "+ %d", dst_reg->Register.Index - ctx->outputs[j].first); + } + snprintf(dsts[i], 255, "clip_dist_temp[%d %s]", ctx->outputs[j].sid, clip_indirect); + } else if (ctx->outputs[j].name == TGSI_SEMANTIC_TESSOUTER || + ctx->outputs[j].name == TGSI_SEMANTIC_TESSINNER || + ctx->outputs[j].name == TGSI_SEMANTIC_SAMPLEMASK) { + int idx; + switch (dst_reg->Register.WriteMask) { + case 0x1: idx = 0; break; + case 0x2: idx = 1; break; + case 0x4: idx = 2; break; + case 0x8: idx = 3; break; + default: + idx = 0; + break; + } + snprintf(dsts[i], 255, "%s[%d]", ctx->outputs[j].glsl_name, idx); + if (ctx->outputs[j].is_int) { + dinfo->dtypeprefix = FLOAT_BITS_TO_INT; + dinfo->dstconv = INT; + } + } else { + if (ctx->outputs[j].glsl_gl_block) { + snprintf(dsts[i], 255, "gl_out[%s].%s%s", + ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL ? "gl_InvocationID" : "0", + ctx->outputs[j].glsl_name, + ctx->outputs[j].override_no_wm ? "" : writemask); + } else if (ctx->outputs[j].name == TGSI_SEMANTIC_GENERIC) { + struct vrend_shader_io *io = ctx->generic_output_range.used ? &ctx->generic_output_range.io : &ctx->outputs[j]; + get_destination_info_generic(ctx, dst_reg, io, writemask, dsts[i]); + dinfo->dst_override_no_wm[i] = ctx->outputs[j].override_no_wm; + } else if (ctx->outputs[j].name == TGSI_SEMANTIC_PATCH) { + struct vrend_shader_io *io = ctx->patch_output_range.used ? &ctx->patch_output_range.io : &ctx->outputs[j]; + char reswizzled[6] = ""; + const char *wm = reswizzle_dest(io, dst_reg, reswizzled, writemask); + if (io->last != io->first) { + if (dst_reg->Register.Indirect) + snprintf(dsts[i], 255, "%s[addr%d + %d]%s", + io->glsl_name, dst_reg->Indirect.Index, + dst_reg->Register.Index - io->first, + io->override_no_wm ? "" : wm); + else + snprintf(dsts[i], 255, "%s[%d]%s", + io->glsl_name, + dst_reg->Register.Index - io->first, + io->override_no_wm ? "" : wm); + } else { + snprintf(dsts[i], 255, "%s%s", io->glsl_name, ctx->outputs[j].override_no_wm ? "" : wm); + } + dinfo->dst_override_no_wm[i] = ctx->outputs[j].override_no_wm; + } else { + if (ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL) { + snprintf(dsts[i], 255, "%s[gl_InvocationID]%s", ctx->outputs[j].glsl_name, ctx->outputs[j].override_no_wm ? "" : writemask); + } else { + snprintf(dsts[i], 255, "%s%s", ctx->outputs[j].glsl_name, ctx->outputs[j].override_no_wm ? "" : writemask); + } + dinfo->dst_override_no_wm[i] = ctx->outputs[j].override_no_wm; + } + if (ctx->outputs[j].is_int) { + if (dinfo->dtypeprefix == TYPE_CONVERSION_NONE) + dinfo->dtypeprefix = FLOAT_BITS_TO_INT; + dinfo->dstconv = INT; + } + if (ctx->outputs[j].name == TGSI_SEMANTIC_PSIZE) { + dinfo->dstconv = FLOAT; + break; + } + } + break; + } + } + } + else if (dst_reg->Register.File == TGSI_FILE_TEMPORARY) { + struct vrend_temp_range *range = find_temp_range(ctx, dst_reg->Register.Index); + if (!range) + return false; + if (dst_reg->Register.Indirect) { + snprintf(dsts[i], 255, "temp%d[addr0 + %d]%s", range->first, dst_reg->Register.Index - range->first, writemask); + } else + snprintf(dsts[i], 255, "temp%d[%d]%s", range->first, dst_reg->Register.Index - range->first, writemask); + } + else if (dst_reg->Register.File == TGSI_FILE_IMAGE) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + if (ctx->info.indirect_files & (1 << TGSI_FILE_IMAGE)) { + int basearrayidx = lookup_image_array(ctx, dst_reg->Register.Index); + if (dst_reg->Register.Indirect) { + assert(dst_reg->Indirect.File == TGSI_FILE_ADDRESS); + snprintf(dsts[i], 255, "%simg%d[addr%d + %d]", cname, basearrayidx, dst_reg->Indirect.Index, dst_reg->Register.Index - basearrayidx); + } else + snprintf(dsts[i], 255, "%simg%d[%d]", cname, basearrayidx, dst_reg->Register.Index - basearrayidx); + } else + snprintf(dsts[i], 255, "%simg%d", cname, dst_reg->Register.Index); + } else if (dst_reg->Register.File == TGSI_FILE_BUFFER) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + if (ctx->info.indirect_files & (1 << TGSI_FILE_BUFFER)) { + bool atomic_ssbo = ctx->ssbo_atomic_mask & (1 << dst_reg->Register.Index); + const char *atomic_str = atomic_ssbo ? "atomic" : ""; + int base = atomic_ssbo ? ctx->ssbo_atomic_array_base : ctx->ssbo_array_base; + if (dst_reg->Register.Indirect) { + snprintf(dsts[i], 255, "%sssboarr%s[addr%d+%d].%sssbocontents%d", cname, atomic_str, dst_reg->Indirect.Index, dst_reg->Register.Index - base, cname, base); + } else + snprintf(dsts[i], 255, "%sssboarr%s[%d].%sssbocontents%d", cname, atomic_str, dst_reg->Register.Index - base, cname, base); + } else + snprintf(dsts[i], 255, "%sssbocontents%d", cname, dst_reg->Register.Index); + } else if (dst_reg->Register.File == TGSI_FILE_MEMORY) { + snprintf(dsts[i], 255, "values"); + } else if (dst_reg->Register.File == TGSI_FILE_ADDRESS) { + snprintf(dsts[i], 255, "addr%d", dst_reg->Register.Index); + } + + if (dtype == TGSI_TYPE_DOUBLE) { + strcpy(fp64_dsts[i], dsts[i]); + snprintf(dsts[i], 255, "fp64_dst[%d]%s", i, fp64_writemask); + writemask[0] = 0; + } + + } + + return true; +} + +static const char *shift_swizzles(const struct vrend_shader_io *io, const struct tgsi_full_src_register *src, + int swz_offset, char *swizzle_shifted, const char *swizzle) +{ + if (io->usage_mask != 0xf && swizzle[0]) { + if (io->num_components > 1) { + swizzle_shifted[swz_offset++] = '.'; + for (int i = 0; i < 4; ++i) { + switch (i) { + case 0: swizzle_shifted[swz_offset++] = get_swiz_char(src->Register.SwizzleX - io->swizzle_offset); + break; + case 1: swizzle_shifted[swz_offset++] = get_swiz_char(src->Register.SwizzleY - io->swizzle_offset); + break; + case 2: swizzle_shifted[swz_offset++] = src->Register.SwizzleZ - io->swizzle_offset < io->num_components ? + get_swiz_char(src->Register.SwizzleZ - io->swizzle_offset) : 'x'; + break; + case 3: swizzle_shifted[swz_offset++] = src->Register.SwizzleW - io->swizzle_offset < io->num_components ? + get_swiz_char(src->Register.SwizzleW - io->swizzle_offset) : 'x'; + } + } + swizzle_shifted[swz_offset] = 0; + } + swizzle = swizzle_shifted; + } + return swizzle; +} + +static void get_source_info_generic(struct dump_ctx *ctx, + enum io_type iot, + enum vrend_type_qualifier srcstypeprefix, + const char *prefix, + const struct tgsi_full_src_register *src, + const struct vrend_shader_io *io, + const char *arrayname, + const char *swizzle, + struct vrend_strbuf *result) +{ + int swz_offset = 0; + char swizzle_shifted[6] = ""; + if (swizzle[0] == ')') { + swizzle_shifted[swz_offset++] = ')'; + swizzle_shifted[swz_offset] = 0; + } + + /* This IO element is not using all vector elements, so we have to shift the swizzle names */ + swizzle = shift_swizzles(io, src, swz_offset, swizzle_shifted, swizzle); + + if (io->first == io->last) { + strbuf_fmt(result, "%s(%s%s%s%s)", get_string(srcstypeprefix), + prefix, io->glsl_name, arrayname, io->is_int ? "" : swizzle); + } else { + + if (prefer_generic_io_block(ctx, iot)) { + char outvarname[64]; + const char *stage_prefix = iot == io_in ? get_stage_input_name_prefix(ctx, ctx->prog_type) : + get_stage_output_name_prefix(ctx->prog_type); + + get_blockvarname(outvarname, stage_prefix, io, arrayname); + if (src->Register.Indirect) + strbuf_fmt(result, "%s(%s %s.%s[addr%d + %d] %s)", get_string(srcstypeprefix), prefix, + outvarname, io->glsl_name, src->Indirect.Index, src->Register.Index - io->first, + io->is_int ? "" : swizzle); + else + strbuf_fmt(result, "%s(%s %s.%s[%d] %s)", get_string(srcstypeprefix), prefix, + outvarname, io->glsl_name, src->Register.Index - io->first, + io->is_int ? "" : swizzle); + } else { + if (src->Register.Indirect) + strbuf_fmt(result, "%s(%s %s%s[addr%d + %d] %s)", get_string(srcstypeprefix), prefix, + io->glsl_name, + arrayname, + src->Indirect.Index, + src->Register.Index - io->first, + io->is_int ? "" : swizzle); + else + strbuf_fmt(result, "%s(%s %s%s[%d] %s)", get_string(srcstypeprefix), prefix, + io->glsl_name, + arrayname, + src->Register.Index - io->first, + io->is_int ? "" : swizzle); + } + + } +} + +static void get_source_info_patch(enum vrend_type_qualifier srcstypeprefix, + const char *prefix, + const struct tgsi_full_src_register *src, + const struct vrend_shader_io *io, + const char *arrayname, + const char *swizzle, + struct vrend_strbuf *result) +{ + int swz_offset = 0; + char swizzle_shifted[7] = ""; + if (swizzle[0] == ')') { + swizzle_shifted[swz_offset++] = ')'; + swizzle_shifted[swz_offset] = 0; + } + + swizzle = shift_swizzles(io, src, swz_offset, swizzle_shifted, swizzle); + const char *wm = io->is_int ? "" : swizzle; + + if (io->last == io->first) + strbuf_fmt(result, "%s(%s%s%s%s)", get_string(srcstypeprefix), prefix, io->glsl_name, + arrayname, wm); + else { + if (src->Register.Indirect) + strbuf_fmt(result, "%s(%s %s[addr%d + %d] %s)", get_string(srcstypeprefix), prefix, + io->glsl_name, src->Indirect.Index, src->Register.Index - io->first, wm); + else + strbuf_fmt(result, "%s(%s %s[%d] %s)", get_string(srcstypeprefix), prefix, + io->glsl_name, src->Register.Index - io->first, wm); + } + +} + + +static bool +get_source_info(struct dump_ctx *ctx, + const struct tgsi_full_instruction *inst, + struct source_info *sinfo, + struct vrend_strbuf srcs[4], char src_swizzle0[10]) +{ + bool stprefix = false; + + enum vrend_type_qualifier stypeprefix = TYPE_CONVERSION_NONE; + enum tgsi_opcode_type stype = tgsi_opcode_infer_src_type(inst->Instruction.Opcode); + + if (stype == TGSI_TYPE_SIGNED || stype == TGSI_TYPE_UNSIGNED) + ctx->shader_req_bits |= SHADER_REQ_INTS; + if (stype == TGSI_TYPE_DOUBLE) + ctx->shader_req_bits |= SHADER_REQ_INTS | SHADER_REQ_FP64; + + switch (stype) { + case TGSI_TYPE_DOUBLE: + stypeprefix = FLOAT_BITS_TO_UINT; + sinfo->svec4 = DVEC2; + stprefix = true; + break; + case TGSI_TYPE_UNSIGNED: + stypeprefix = FLOAT_BITS_TO_UINT; + sinfo->svec4 = UVEC4; + stprefix = true; + break; + case TGSI_TYPE_SIGNED: + stypeprefix = FLOAT_BITS_TO_INT; + sinfo->svec4 = IVEC4; + stprefix = true; + break; + } + + for (uint32_t i = 0; i < inst->Instruction.NumSrcRegs; i++) { + const struct tgsi_full_src_register *src = &inst->Src[i]; + struct vrend_strbuf *src_buf = &srcs[i]; + char swizzle[8] = ""; + int usage_mask = 0; + char *swizzle_writer = swizzle; + char prefix[6] = ""; + char arrayname[16] = ""; + char fp64_src[255]; + int swz_idx = 0, pre_idx = 0; + boolean isfloatabsolute = src->Register.Absolute && stype != TGSI_TYPE_DOUBLE; + + sinfo->override_no_wm[i] = false; + sinfo->override_no_cast[i] = false; + if (isfloatabsolute) + swizzle[swz_idx++] = ')'; + + if (src->Register.Negate) + prefix[pre_idx++] = '-'; + if (isfloatabsolute) + strcpy(&prefix[pre_idx++], "abs("); + + if (src->Register.Dimension) { + if (src->Dimension.Indirect) { + assert(src->DimIndirect.File == TGSI_FILE_ADDRESS); + sprintf(arrayname, "[addr%d]", src->DimIndirect.Index); + } else + sprintf(arrayname, "[%d]", src->Dimension.Index); + } + + /* These instructions don't support swizzles in the first parameter + * pass the swizzle to the caller instead */ + if ((inst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE || + inst->Instruction.Opcode == TGSI_OPCODE_INTERP_OFFSET || + inst->Instruction.Opcode == TGSI_OPCODE_INTERP_CENTROID) && + i == 0) { + swizzle_writer = src_swizzle0; + } + + usage_mask |= 1 << src->Register.SwizzleX; + usage_mask |= 1 << src->Register.SwizzleY; + usage_mask |= 1 << src->Register.SwizzleZ; + usage_mask |= 1 << src->Register.SwizzleW; + + if (src->Register.SwizzleX != TGSI_SWIZZLE_X || + src->Register.SwizzleY != TGSI_SWIZZLE_Y || + src->Register.SwizzleZ != TGSI_SWIZZLE_Z || + src->Register.SwizzleW != TGSI_SWIZZLE_W) { + swizzle_writer[swz_idx++] = '.'; + swizzle_writer[swz_idx++] = get_swiz_char(src->Register.SwizzleX); + swizzle_writer[swz_idx++] = get_swiz_char(src->Register.SwizzleY); + swizzle_writer[swz_idx++] = get_swiz_char(src->Register.SwizzleZ); + swizzle_writer[swz_idx++] = get_swiz_char(src->Register.SwizzleW); + } + swizzle_writer[swz_idx] = 0; + + if (src->Register.File == TGSI_FILE_INPUT) { + for (uint32_t j = 0; j < ctx->num_inputs; j++) + if (ctx->inputs[j].first <= src->Register.Index && + ctx->inputs[j].last >= src->Register.Index && + (ctx->inputs[j].usage_mask & usage_mask)) { + if (ctx->key->color_two_side && ctx->inputs[j].name == TGSI_SEMANTIC_COLOR) + strbuf_fmt(src_buf, "%s(%s%s%d%s%s)", get_string(stypeprefix), prefix, "realcolor", ctx->inputs[j].sid, arrayname, swizzle); + else if (ctx->inputs[j].glsl_gl_block) { + /* GS input clipdist requires a conversion */ + if (ctx->inputs[j].name == TGSI_SEMANTIC_CLIPDIST) { + create_swizzled_clipdist(ctx, src_buf, src, j, true, get_string(stypeprefix), prefix, arrayname, ctx->inputs[j].first); + } else { + strbuf_fmt(src_buf, "%s(vec4(%sgl_in%s.%s)%s)", get_string(stypeprefix), prefix, arrayname, ctx->inputs[j].glsl_name, swizzle); + } + } + else if (ctx->inputs[j].name == TGSI_SEMANTIC_PRIMID) + strbuf_fmt(src_buf, "%s(vec4(intBitsToFloat(%s)))", get_string(stypeprefix), ctx->inputs[j].glsl_name); + else if (ctx->inputs[j].name == TGSI_SEMANTIC_FACE) + strbuf_fmt(src_buf, "%s(%s ? 1.0 : -1.0)", get_string(stypeprefix), ctx->inputs[j].glsl_name); + else if (ctx->inputs[j].name == TGSI_SEMANTIC_CLIPDIST) { + if (ctx->prog_type == TGSI_PROCESSOR_FRAGMENT) + load_clipdist_fs(ctx, src_buf, src, j, false, get_string(stypeprefix), ctx->inputs[j].first); + else + create_swizzled_clipdist(ctx, src_buf, src, j, false, get_string(stypeprefix), prefix, arrayname, ctx->inputs[j].first); + } else { + enum vrend_type_qualifier srcstypeprefix = stypeprefix; + if ((stype == TGSI_TYPE_UNSIGNED || stype == TGSI_TYPE_SIGNED) && + ctx->inputs[j].is_int) + srcstypeprefix = TYPE_CONVERSION_NONE; + + if (inst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE && i == 1) { + strbuf_fmt(src_buf, "floatBitsToInt(%s%s%s%s)", prefix, ctx->inputs[j].glsl_name, arrayname, swizzle); + } else if (ctx->inputs[j].name == TGSI_SEMANTIC_GENERIC) { + struct vrend_shader_io *io = ctx->generic_input_range.used ? &ctx->generic_input_range.io : &ctx->inputs[j]; + get_source_info_generic(ctx, io_in, srcstypeprefix, prefix, src, io, arrayname, swizzle, src_buf); + } else if (ctx->inputs[j].name == TGSI_SEMANTIC_PATCH) { + struct vrend_shader_io *io = ctx->patch_input_range.used ? &ctx->patch_input_range.io : &ctx->inputs[j]; + get_source_info_patch(srcstypeprefix, prefix, src, io, arrayname, swizzle, src_buf); + } else if (ctx->inputs[j].name == TGSI_SEMANTIC_POSITION && ctx->prog_type == TGSI_PROCESSOR_VERTEX && + ctx->inputs[j].first != ctx->inputs[j].last) { + if (src->Register.Indirect) + strbuf_fmt(src_buf, "%s(%s%s%s[addr%d + %d]%s)", get_string(srcstypeprefix), prefix, ctx->inputs[j].glsl_name, arrayname, + src->Indirect.Index, src->Register.Index, ctx->inputs[j].is_int ? "" : swizzle); + else + strbuf_fmt(src_buf, "%s(%s%s%s[%d]%s)", get_string(srcstypeprefix), prefix, ctx->inputs[j].glsl_name, arrayname, + src->Register.Index, ctx->inputs[j].is_int ? "" : swizzle); + } else + strbuf_fmt(src_buf, "%s(%s%s%s%s)", get_string(srcstypeprefix), prefix, ctx->inputs[j].glsl_name, arrayname, ctx->inputs[j].is_int ? "" : swizzle); + } + sinfo->override_no_wm[i] = ctx->inputs[j].override_no_wm; + break; + } + } else if (src->Register.File == TGSI_FILE_OUTPUT) { + for (uint32_t j = 0; j < ctx->num_outputs; j++) { + if (ctx->outputs[j].first <= src->Register.Index && + ctx->outputs[j].last >= src->Register.Index && + (ctx->outputs[j].usage_mask & usage_mask)) { + if (inst->Instruction.Opcode == TGSI_OPCODE_FBFETCH) { + ctx->outputs[j].fbfetch_used = true; + ctx->shader_req_bits |= SHADER_REQ_FBFETCH; + } + + enum vrend_type_qualifier srcstypeprefix = stypeprefix; + if (stype == TGSI_TYPE_UNSIGNED && ctx->outputs[j].is_int) + srcstypeprefix = TYPE_CONVERSION_NONE; + if (ctx->outputs[j].glsl_gl_block) { + if (ctx->outputs[j].name == TGSI_SEMANTIC_CLIPDIST) { + char clip_indirect[32] = ""; + if (ctx->outputs[j].first != ctx->outputs[j].last) { + if (src->Register.Indirect) + snprintf(clip_indirect, sizeof(clip_indirect), "+ addr%d", src->Indirect.Index); + else + snprintf(clip_indirect, sizeof(clip_indirect), "+ %d", src->Register.Index - ctx->outputs[j].first); + } + strbuf_fmt(src_buf, "clip_dist_temp[%d%s]", ctx->outputs[j].sid, clip_indirect); + } + } else if (ctx->outputs[j].name == TGSI_SEMANTIC_GENERIC) { + struct vrend_shader_io *io = ctx->generic_output_range.used ? &ctx->generic_output_range.io : &ctx->outputs[j]; + get_source_info_generic(ctx, io_out, srcstypeprefix, prefix, src, io, arrayname, swizzle, src_buf); + } else if (ctx->outputs[j].name == TGSI_SEMANTIC_PATCH) { + struct vrend_shader_io *io = ctx->patch_output_range.used ? &ctx->patch_output_range.io : &ctx->outputs[j]; + get_source_info_patch(srcstypeprefix, prefix, src, io, arrayname, swizzle, src_buf); + } else { + strbuf_fmt(src_buf, "%s(%s%s%s%s)", get_string(srcstypeprefix), prefix, ctx->outputs[j].glsl_name, arrayname, ctx->outputs[j].is_int ? "" : swizzle); + } + sinfo->override_no_wm[i] = ctx->outputs[j].override_no_wm; + break; + } + } + } else if (src->Register.File == TGSI_FILE_TEMPORARY) { + struct vrend_temp_range *range = find_temp_range(ctx, src->Register.Index); + if (!range) + return false; + if (inst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE && i == 1) { + stprefix = true; + stypeprefix = FLOAT_BITS_TO_INT; + } + + if (src->Register.Indirect) { + assert(src->Indirect.File == TGSI_FILE_ADDRESS); + strbuf_fmt(src_buf, "%s%c%stemp%d[addr%d + %d]%s%c", get_string(stypeprefix), stprefix ? '(' : ' ', prefix, range->first, src->Indirect.Index, src->Register.Index - range->first, swizzle, stprefix ? ')' : ' '); + } else + strbuf_fmt(src_buf, "%s%c%stemp%d[%d]%s%c", get_string(stypeprefix), stprefix ? '(' : ' ', prefix, range->first, src->Register.Index - range->first, swizzle, stprefix ? ')' : ' '); + } else if (src->Register.File == TGSI_FILE_CONSTANT) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + int dim = 0; + if (src->Register.Dimension && src->Dimension.Index != 0) { + dim = src->Dimension.Index; + if (src->Dimension.Indirect) { + assert(src->DimIndirect.File == TGSI_FILE_ADDRESS); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + if (src->Register.Indirect) { + assert(src->Indirect.File == TGSI_FILE_ADDRESS); + strbuf_fmt(src_buf, "%s(%s%suboarr[addr%d].ubocontents[addr%d + %d]%s)", get_string(stypeprefix), prefix, cname, src->DimIndirect.Index, src->Indirect.Index, src->Register.Index, swizzle); + } else + strbuf_fmt(src_buf, "%s(%s%suboarr[addr%d].ubocontents[%d]%s)", get_string(stypeprefix), prefix, cname, src->DimIndirect.Index, src->Register.Index, swizzle); + } else { + if (ctx->info.dimension_indirect_files & (1 << TGSI_FILE_CONSTANT)) { + if (src->Register.Indirect) { + strbuf_fmt(src_buf, "%s(%s%suboarr[%d].ubocontents[addr%d + %d]%s)", get_string(stypeprefix), prefix, cname, dim - ctx->ubo_base, src->Indirect.Index, src->Register.Index, swizzle); + } else + strbuf_fmt(src_buf, "%s(%s%suboarr[%d].ubocontents[%d]%s)", get_string(stypeprefix), prefix, cname, dim - ctx->ubo_base, src->Register.Index, swizzle); + } else { + if (src->Register.Indirect) { + strbuf_fmt(src_buf, "%s(%s%subo%dcontents[addr0 + %d]%s)", get_string(stypeprefix), prefix, cname, dim, src->Register.Index, swizzle); + } else + strbuf_fmt(src_buf, "%s(%s%subo%dcontents[%d]%s)", get_string(stypeprefix), prefix, cname, dim, src->Register.Index, swizzle); + } + } + } else { + enum vrend_type_qualifier csp = TYPE_CONVERSION_NONE; + ctx->shader_req_bits |= SHADER_REQ_INTS; + if (inst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE && i == 1) + csp = IVEC4; + else if (stype == TGSI_TYPE_FLOAT || stype == TGSI_TYPE_UNTYPED) + csp = UINT_BITS_TO_FLOAT; + else if (stype == TGSI_TYPE_SIGNED) + csp = IVEC4; + + if (src->Register.Indirect) { + strbuf_fmt(src_buf, "%s%s(%sconst%d[addr0 + %d]%s)", prefix, get_string(csp), cname, dim, src->Register.Index, swizzle); + } else + strbuf_fmt(src_buf, "%s%s(%sconst%d[%d]%s)", prefix, get_string(csp), cname, dim, src->Register.Index, swizzle); + } + } else if (src->Register.File == TGSI_FILE_SAMPLER) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + if (ctx->info.indirect_files & (1 << TGSI_FILE_SAMPLER)) { + int basearrayidx = lookup_sampler_array(ctx, src->Register.Index); + if (src->Register.Indirect) { + strbuf_fmt(src_buf, "%ssamp%d[addr%d+%d]%s", cname, basearrayidx, src->Indirect.Index, src->Register.Index - basearrayidx, swizzle); + } else { + strbuf_fmt(src_buf, "%ssamp%d[%d]%s", cname, basearrayidx, src->Register.Index - basearrayidx, swizzle); + } + } else { + strbuf_fmt(src_buf, "%ssamp%d%s", cname, src->Register.Index, swizzle); + } + sinfo->sreg_index = src->Register.Index; + } else if (src->Register.File == TGSI_FILE_IMAGE) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + if (ctx->info.indirect_files & (1 << TGSI_FILE_IMAGE)) { + int basearrayidx = lookup_image_array(ctx, src->Register.Index); + if (src->Register.Indirect) { + assert(src->Indirect.File == TGSI_FILE_ADDRESS); + strbuf_fmt(src_buf, "%simg%d[addr%d + %d]", cname, basearrayidx, src->Indirect.Index, src->Register.Index - basearrayidx); + } else + strbuf_fmt(src_buf, "%simg%d[%d]", cname, basearrayidx, src->Register.Index - basearrayidx); + } else + strbuf_fmt(src_buf, "%simg%d%s", cname, src->Register.Index, swizzle); + sinfo->sreg_index = src->Register.Index; + } else if (src->Register.File == TGSI_FILE_BUFFER) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + if (ctx->info.indirect_files & (1 << TGSI_FILE_BUFFER)) { + bool atomic_ssbo = ctx->ssbo_atomic_mask & (1 << src->Register.Index); + const char *atomic_str = atomic_ssbo ? "atomic" : ""; + int base = atomic_ssbo ? ctx->ssbo_atomic_array_base : ctx->ssbo_array_base; + if (src->Register.Indirect) { + strbuf_fmt(src_buf, "%sssboarr%s[addr%d+%d].%sssbocontents%d%s", cname, atomic_str, src->Indirect.Index, src->Register.Index - base, cname, base, swizzle); + } else { + strbuf_fmt(src_buf, "%sssboarr%s[%d].%sssbocontents%d%s", cname, atomic_str, src->Register.Index - base, cname, base, swizzle); + } + } else { + strbuf_fmt(src_buf, "%sssbocontents%d%s", cname, src->Register.Index, swizzle); + } + sinfo->sreg_index = src->Register.Index; + } else if (src->Register.File == TGSI_FILE_MEMORY) { + strbuf_fmt(src_buf, "values"); + sinfo->sreg_index = src->Register.Index; + } else if (src->Register.File == TGSI_FILE_IMMEDIATE) { + if (src->Register.Index >= (int)ARRAY_SIZE(ctx->imm)) + return false; + struct immed *imd = &ctx->imm[src->Register.Index]; + int idx = src->Register.SwizzleX; + char temp[48]; + enum vrend_type_qualifier vtype = VEC4; + enum vrend_type_qualifier imm_stypeprefix = stypeprefix; + + if ((inst->Instruction.Opcode == TGSI_OPCODE_TG4 && i == 1) || + (inst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE && i == 1)) + stype = TGSI_TYPE_SIGNED; + + if (imd->type == TGSI_IMM_UINT32 || imd->type == TGSI_IMM_INT32) { + if (imd->type == TGSI_IMM_UINT32) + vtype = UVEC4; + else + vtype = IVEC4; + + if (stype == TGSI_TYPE_UNSIGNED && imd->type == TGSI_IMM_INT32) + imm_stypeprefix = UVEC4; + else if (stype == TGSI_TYPE_SIGNED && imd->type == TGSI_IMM_UINT32) + imm_stypeprefix = IVEC4; + else if (stype == TGSI_TYPE_FLOAT || stype == TGSI_TYPE_UNTYPED) { + if (imd->type == TGSI_IMM_INT32) + imm_stypeprefix = INT_BITS_TO_FLOAT; + else + imm_stypeprefix = UINT_BITS_TO_FLOAT; + } else if (stype == TGSI_TYPE_UNSIGNED || stype == TGSI_TYPE_SIGNED) + imm_stypeprefix = TYPE_CONVERSION_NONE; + } else if (imd->type == TGSI_IMM_FLOAT64) { + vtype = UVEC4; + if (stype == TGSI_TYPE_DOUBLE) + imm_stypeprefix = TYPE_CONVERSION_NONE; + else + imm_stypeprefix = UINT_BITS_TO_FLOAT; + } + + /* build up a vec4 of immediates */ + strbuf_fmt(src_buf, "%s(%s%s(", get_string(imm_stypeprefix), prefix, get_string(vtype)); + for (uint32_t j = 0; j < 4; j++) { + if (j == 0) + idx = src->Register.SwizzleX; + else if (j == 1) + idx = src->Register.SwizzleY; + else if (j == 2) + idx = src->Register.SwizzleZ; + else if (j == 3) + idx = src->Register.SwizzleW; + + if (inst->Instruction.Opcode == TGSI_OPCODE_TG4 && i == 1 && j == 0) { + if (imd->val[idx].ui > 0) + sinfo->tg4_has_component = true; + } + + switch (imd->type) { + case TGSI_IMM_FLOAT32: + if (isinf(imd->val[idx].f) || isnan(imd->val[idx].f)) { + ctx->shader_req_bits |= SHADER_REQ_INTS; + snprintf(temp, 48, "uintBitsToFloat(%uU)", imd->val[idx].ui); + } else + snprintf(temp, 25, "%.8g", imd->val[idx].f); + break; + case TGSI_IMM_UINT32: + snprintf(temp, 25, "%uU", imd->val[idx].ui); + break; + case TGSI_IMM_INT32: + snprintf(temp, 25, "%d", imd->val[idx].i); + sinfo->imm_value = imd->val[idx].i; + break; + case TGSI_IMM_FLOAT64: + snprintf(temp, 48, "%uU", imd->val[idx].ui); + break; + default: + return false; + } + strbuf_append(src_buf, temp); + if (j < 3) + strbuf_append(src_buf, ","); + else { + snprintf(temp, 4, "))%c", isfloatabsolute ? ')' : 0); + strbuf_append(src_buf, temp); + } + } + } else if (src->Register.File == TGSI_FILE_SYSTEM_VALUE) { + for (uint32_t j = 0; j < ctx->num_system_values; j++) + if (ctx->system_values[j].first == src->Register.Index) { + if (ctx->system_values[j].name == TGSI_SEMANTIC_VERTEXID || + ctx->system_values[j].name == TGSI_SEMANTIC_INSTANCEID || + ctx->system_values[j].name == TGSI_SEMANTIC_PRIMID || + ctx->system_values[j].name == TGSI_SEMANTIC_VERTICESIN || + ctx->system_values[j].name == TGSI_SEMANTIC_INVOCATIONID || + ctx->system_values[j].name == TGSI_SEMANTIC_SAMPLEID) { + if (inst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE && i == 1) + strbuf_fmt(src_buf, "ivec4(%s)", ctx->system_values[j].glsl_name); + else + strbuf_fmt(src_buf, "%s(vec4(intBitsToFloat(%s)))", get_string(stypeprefix), ctx->system_values[j].glsl_name); + } else if (ctx->system_values[j].name == TGSI_SEMANTIC_HELPER_INVOCATION) { + strbuf_fmt(src_buf, "uvec4(%s)", ctx->system_values[j].glsl_name); + } else if (ctx->system_values[j].name == TGSI_SEMANTIC_TESSINNER || + ctx->system_values[j].name == TGSI_SEMANTIC_TESSOUTER) { + strbuf_fmt(src_buf, "%s(vec4(%s[%d], %s[%d], %s[%d], %s[%d]))", + prefix, + ctx->system_values[j].glsl_name, src->Register.SwizzleX, + ctx->system_values[j].glsl_name, src->Register.SwizzleY, + ctx->system_values[j].glsl_name, src->Register.SwizzleZ, + ctx->system_values[j].glsl_name, src->Register.SwizzleW); + } else if (ctx->system_values[j].name == TGSI_SEMANTIC_SAMPLEPOS) { + /* gl_SamplePosition is a vec2, but TGSI_SEMANTIC_SAMPLEPOS + * is a vec4 with z = w = 0 + */ + const char *components[4] = { + "gl_SamplePosition.x", "gl_SamplePosition.y", "0.0", "0.0" + }; + strbuf_fmt(src_buf, "%s(vec4(%s, %s, %s, %s))", + prefix, + components[src->Register.SwizzleX], + components[src->Register.SwizzleY], + components[src->Register.SwizzleZ], + components[src->Register.SwizzleW]); + } else if (ctx->system_values[j].name == TGSI_SEMANTIC_TESSCOORD) { + strbuf_fmt(src_buf, "%s(vec4(%s.%c, %s.%c, %s.%c, %s.%c))", + prefix, + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleX), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleY), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleZ), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleW)); + } else if (ctx->system_values[j].name == TGSI_SEMANTIC_GRID_SIZE || + ctx->system_values[j].name == TGSI_SEMANTIC_THREAD_ID || + ctx->system_values[j].name == TGSI_SEMANTIC_BLOCK_ID) { + enum vrend_type_qualifier mov_conv = TYPE_CONVERSION_NONE; + if (inst->Instruction.Opcode == TGSI_OPCODE_MOV && + inst->Dst[0].Register.File == TGSI_FILE_TEMPORARY) + mov_conv = UINT_BITS_TO_FLOAT; + strbuf_fmt(src_buf, "%s(uvec4(%s.%c, %s.%c, %s.%c, %s.%c))", get_string(mov_conv), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleX), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleY), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleZ), + ctx->system_values[j].glsl_name, get_swiz_char(src->Register.SwizzleW)); + sinfo->override_no_cast[i] = true; + } else if (ctx->system_values[j].name == TGSI_SEMANTIC_SAMPLEMASK) { + const char *vec_type = "ivec4"; + if ((inst->Instruction.Opcode == TGSI_OPCODE_AND) && + (stype == TGSI_TYPE_UNSIGNED)) + vec_type = "uvec4"; + ctx->shader_req_bits |= SHADER_REQ_SAMPLE_SHADING | SHADER_REQ_INTS; + strbuf_fmt(src_buf, "%s(%s, %s, %s, %s)", + vec_type, + src->Register.SwizzleX == TGSI_SWIZZLE_X ? ctx->system_values[j].glsl_name : "0", + src->Register.SwizzleY == TGSI_SWIZZLE_X ? ctx->system_values[j].glsl_name : "0", + src->Register.SwizzleZ == TGSI_SWIZZLE_X ? ctx->system_values[j].glsl_name : "0", + src->Register.SwizzleW == TGSI_SWIZZLE_X ? ctx->system_values[j].glsl_name : "0"); + } else + strbuf_fmt(src_buf, "%s%s", prefix, ctx->system_values[j].glsl_name); + sinfo->override_no_wm[i] = ctx->system_values[j].override_no_wm; + break; + } + } else if (src->Register.File == TGSI_FILE_HW_ATOMIC) { + for (uint32_t j = 0; j < ctx->num_abo; j++) { + if (src->Dimension.Index == ctx->abo_idx[j] && + src->Register.Index >= ctx->abo_offsets[j] && + src->Register.Index < ctx->abo_offsets[j] + ctx->abo_sizes[j]) { + if (ctx->abo_sizes[j] > 1) { + int offset = src->Register.Index - ctx->abo_offsets[j]; + if (src->Register.Indirect) { + assert(src->Indirect.File == TGSI_FILE_ADDRESS); + strbuf_fmt(src_buf, "ac%d[addr%d + %d]", j, src->Indirect.Index, offset); + } else + strbuf_fmt(src_buf, "ac%d[%d]", j, offset); + } else + strbuf_fmt(src_buf, "ac%d", j); + break; + } + } + sinfo->sreg_index = src->Register.Index; + } + + if (stype == TGSI_TYPE_DOUBLE) { + boolean isabsolute = src->Register.Absolute; + strcpy(fp64_src, src_buf->buf); + strbuf_fmt(src_buf, "fp64_src[%d]", i); + emit_buff(ctx, "%s.x = %spackDouble2x32(uvec2(%s%s))%s;\n", src_buf->buf, isabsolute ? "abs(" : "", fp64_src, swizzle, isabsolute ? ")" : ""); + } + } + + return true; +} + +static bool rewrite_1d_image_coordinate(struct vrend_strbuf *src, const struct tgsi_full_instruction *inst) +{ + if (inst->Src[0].Register.File == TGSI_FILE_IMAGE && + (inst->Memory.Texture == TGSI_TEXTURE_1D || + inst->Memory.Texture == TGSI_TEXTURE_1D_ARRAY)) { + + /* duplicate src */ + size_t len = strbuf_get_len(src); + char *buf = malloc(len); + if (!buf) + return false; + strncpy(buf, src->buf, len); + + if (inst->Memory.Texture == TGSI_TEXTURE_1D) + strbuf_fmt(src, "vec2(vec4(%s).x, 0)", buf); + else if (inst->Memory.Texture == TGSI_TEXTURE_1D_ARRAY) + strbuf_fmt(src, "vec3(%s.xy, 0).xzy", buf); + + free(buf); + } + return true; +} +/* We have indirect IO access, but the guest actually send separate values, so + * now we have to emulate an array. + */ +static +void rewrite_io_ranged(struct dump_ctx *ctx) +{ + if ((ctx->info.indirect_files & (1 << TGSI_FILE_INPUT)) || + ctx->key->num_indirect_generic_inputs || + ctx->key->num_indirect_patch_inputs) { + + for (uint i = 0; i < ctx->num_inputs; ++i) { + if (ctx->inputs[i].name == TGSI_SEMANTIC_PATCH) { + ctx->inputs[i].glsl_predefined_no_emit = true; + if (ctx->inputs[i].sid < ctx->patch_input_range.io.sid || ctx->patch_input_range.used == false) { + ctx->patch_input_range.io.first = i; + ctx->patch_input_range.io.usage_mask = 0xf; + ctx->patch_input_range.io.name = TGSI_SEMANTIC_PATCH; + ctx->patch_input_range.io.sid = ctx->inputs[i].sid; + ctx->patch_input_range.used = true; + } + if (ctx->inputs[i].sid > ctx->patch_input_range.io.last) + ctx->patch_input_range.io.last = ctx->inputs[i].sid; + } + + if (ctx->inputs[i].name == TGSI_SEMANTIC_GENERIC) { + ctx->inputs[i].glsl_predefined_no_emit = true; + if (ctx->inputs[i].sid < ctx->generic_input_range.io.sid || ctx->generic_input_range.used == false) { + ctx->generic_input_range.io.sid = ctx->inputs[i].sid; + ctx->generic_input_range.io.first = i; + ctx->generic_input_range.io.name = TGSI_SEMANTIC_GENERIC; + ctx->generic_input_range.io.num_components = 4; + ctx->generic_input_range.used = true; + } + if (ctx->inputs[i].sid > ctx->generic_input_range.io.last) + ctx->generic_input_range.io.last = ctx->inputs[i].sid; + } + + if (ctx->key->num_indirect_generic_inputs > 0) + ctx->generic_input_range.io.last = ctx->generic_input_range.io.sid + ctx->key->num_indirect_generic_inputs - 1; + if (ctx->key->num_indirect_patch_inputs > 0) + ctx->patch_input_range.io.last = ctx->patch_input_range.io.sid + ctx->key->num_indirect_patch_inputs - 1; + } + snprintf(ctx->patch_input_range.io.glsl_name, 64, "%s_p%d", + get_stage_input_name_prefix(ctx, ctx->prog_type), ctx->patch_input_range.io.sid); + snprintf(ctx->generic_input_range.io.glsl_name, 64, "%s_g%d", + get_stage_input_name_prefix(ctx, ctx->prog_type), ctx->generic_input_range.io.sid); + + ctx->generic_input_range.io.num_components = 4; + ctx->generic_input_range.io.usage_mask = 0xf; + ctx->generic_input_range.io.swizzle_offset = 0; + + ctx->patch_input_range.io.num_components = 4; + ctx->patch_input_range.io.usage_mask = 0xf; + ctx->patch_input_range.io.swizzle_offset = 0; + + if (prefer_generic_io_block(ctx, io_in)) + require_glsl_ver(ctx, 150); + } + + if ((ctx->info.indirect_files & (1 << TGSI_FILE_OUTPUT)) || + ctx->key->num_indirect_generic_outputs || + ctx->key->num_indirect_patch_outputs) { + + for (uint i = 0; i < ctx->num_outputs; ++i) { + if (ctx->outputs[i].name == TGSI_SEMANTIC_PATCH) { + ctx->outputs[i].glsl_predefined_no_emit = true; + if (ctx->outputs[i].sid < ctx->patch_output_range.io.sid || ctx->patch_output_range.used == false) { + ctx->patch_output_range.io.first = i; + ctx->patch_output_range.io.name = TGSI_SEMANTIC_PATCH; + ctx->patch_output_range.io.sid = ctx->outputs[i].sid; + ctx->patch_output_range.used = true; + } + if (ctx->outputs[i].sid > ctx->patch_output_range.io.last) { + ctx->patch_output_range.io.last = ctx->outputs[i].sid; + } + } + + if (ctx->outputs[i].name == TGSI_SEMANTIC_GENERIC) { + ctx->outputs[i].glsl_predefined_no_emit = true; + if (ctx->outputs[i].sid < ctx->generic_output_range.io.sid || ctx->generic_output_range.used == false) { + ctx->generic_output_range.io.sid = ctx->outputs[i].sid; + ctx->generic_output_range.io.first = i; + ctx->generic_output_range.io.name = TGSI_SEMANTIC_GENERIC; + ctx->generic_output_range.used = true; + ctx->generic_output_range.io.usage_mask = 0xf; + ctx->generic_output_range.io.num_components = 4; + } + if (ctx->outputs[i].sid > ctx->generic_output_range.io.last) { + ctx->generic_output_range.io.last = ctx->outputs[i].sid; + } + } + } + snprintf(ctx->patch_output_range.io.glsl_name, 64, "%s_p%d", + get_stage_output_name_prefix(ctx->prog_type), ctx->patch_output_range.io.sid); + snprintf(ctx->generic_output_range.io.glsl_name, 64, "%s_g%d", + get_stage_output_name_prefix(ctx->prog_type), ctx->generic_output_range.io.sid); + + ctx->generic_output_range.io.num_components = 4; + ctx->generic_output_range.io.usage_mask = 0xf; + ctx->generic_output_range.io.swizzle_offset = 0; + + ctx->patch_output_range.io.num_components = 4; + ctx->patch_output_range.io.usage_mask = 0xf; + ctx->patch_output_range.io.swizzle_offset = 0; + + + if (prefer_generic_io_block(ctx, io_out)) + require_glsl_ver(ctx, 150); + } +} + + +static void rename_variables(unsigned nio, struct vrend_shader_io *io, + const char *name_prefix, unsigned coord_replace) +{ + /* Rename the generic and patch variables after applying all identifications */ + for (unsigned i = 0; i < nio; ++i) { + if ((io[i].name != TGSI_SEMANTIC_GENERIC && + io[i].name != TGSI_SEMANTIC_PATCH) || + (coord_replace & (1 << io[i].sid))) + continue; + char io_type = io[i].name == TGSI_SEMANTIC_GENERIC ? 'g' : 'p'; + snprintf(io[i].glsl_name, 64, "%s_%c%dA%d_%x", name_prefix, io_type, io[i].sid, io[i].array_id, io[i].usage_mask); + } +} + +static +void rewrite_components(unsigned nio, struct vrend_shader_io *io, + const char *name_prefix, unsigned coord_replace, + bool no_input_arrays) +{ + if (!nio) + return; + + for (unsigned i = 0; i < nio - 1; ++i) { + if ((io[i].name != TGSI_SEMANTIC_GENERIC && + io[i].name != TGSI_SEMANTIC_PATCH) || + io[i].glsl_predefined_no_emit) + continue; + + for (unsigned j = i + 1; j < nio; ++j) { + if ((io[j].name != TGSI_SEMANTIC_GENERIC && + io[j].name != TGSI_SEMANTIC_PATCH) || + io[j].glsl_predefined_no_emit) + continue; + if (io[i].first == io[j].first) + io[j].glsl_predefined_no_emit = true; + } + } + + for (unsigned i = 0; i < nio; ++i) { + if ((io[i].name != TGSI_SEMANTIC_GENERIC && + io[i].name != TGSI_SEMANTIC_PATCH) || + !no_input_arrays) + continue; + + io[i].usage_mask = 0xf; + io[i].num_components = 4; + io[i].swizzle_offset = 0; + io[i].override_no_wm = false; + } + + rename_variables(nio, io, name_prefix, coord_replace); +} + +static +void rewrite_vs_pos_array(struct dump_ctx *ctx) +{ + int range_start = 0xffff; + int range_end = 0; + int io_idx = 0; + + for (uint i = 0; i < ctx->num_inputs; ++i) { + if (ctx->inputs[i].name == TGSI_SEMANTIC_POSITION) { + ctx->inputs[i].glsl_predefined_no_emit = true; + if (ctx->inputs[i].first < range_start) { + io_idx = i; + range_start = ctx->inputs[i].first; + } + if (ctx->inputs[i].last > range_end) + range_end = ctx->inputs[i].last; + } + } + + if (range_start != range_end) { + ctx->inputs[io_idx].first = range_start; + ctx->inputs[io_idx].last = range_end; + ctx->inputs[io_idx].glsl_predefined_no_emit = false; + require_glsl_ver(ctx, 150); + } +} + + +static +void emit_fs_clipdistance_load(struct dump_ctx *ctx) +{ + int i; + + if (!ctx->fs_uses_clipdist_input) + return; + + int prev_num = ctx->key->prev_stage_num_clip_out + ctx->key->prev_stage_num_cull_out; + int ndists; + const char *prefix=""; + + if (ctx->prog_type == PIPE_SHADER_TESS_CTRL) + prefix = "gl_out[gl_InvocationID]."; + + ndists = ctx->num_in_clip_dist; + if (prev_num > 0) + ndists = prev_num; + + for (i = 0; i < ndists; i++) { + int clipidx = i < 4 ? 0 : 1; + char swiz = i & 3; + char wm = 0; + switch (swiz) { + default: + case 0: wm = 'x'; break; + case 1: wm = 'y'; break; + case 2: wm = 'z'; break; + case 3: wm = 'w'; break; + } + bool is_cull = false; + if (prev_num > 0) { + if (i >= ctx->key->prev_stage_num_clip_out && i < prev_num) + is_cull = true; + } + const char *clip_cull = is_cull ? "Cull" : "Clip"; + emit_buff(ctx, "clip_dist_temp[%d].%c = %sgl_%sDistance[%d];\n", clipidx, wm, prefix, clip_cull, + is_cull ? i - ctx->key->prev_stage_num_clip_out : i); + } +} + +/* TGSI possibly emits VS, TES, TCS, and GEOM outputs with layouts (i.e. + * it gives components), but it doesn't do so for the corresponding inputs from + * TXS, GEOM, abd TES, so that we have to apply the output layouts from the + * previous shader stage to the according inputs. + */ + +static bool apply_prev_layout(struct dump_ctx *ctx) +{ + bool require_enhanced_layouts = false; + + /* Walk through all inputs and see whether we have a corresonding output from + * the previous shader that uses a different layout. It may even be that one + * input be the combination of two inputs. */ + + for (unsigned i = 0; i < ctx->num_inputs; ++i ) { + unsigned i_input = i; + struct vrend_shader_io *io = &ctx->inputs[i]; + + if (io->name == TGSI_SEMANTIC_GENERIC || io->name == TGSI_SEMANTIC_PATCH) { + + struct vrend_layout_info *layout = ctx->key->prev_stage_generic_and_patch_outputs_layout; + for (unsigned generic_index = 0; generic_index < ctx->key->num_prev_generic_and_patch_outputs; ++generic_index, ++layout) { + + bool already_found_one = false; + + /* Identify by sid and arrays_id */ + if (io->sid == layout->sid && (io->array_id == layout->array_id)) { + unsigned new_mask = io->usage_mask; + + /* We have already one IO with the same SID and arrays ID, so we need to duplicate it */ + if (already_found_one) { + memmove(io + 1, io, (ctx->num_inputs - i_input) * sizeof(struct vrend_shader_io)); + ctx->num_inputs++; + ++io; + ++i_input; + + } else if ((io->usage_mask == 0xf) && (layout->usage_mask != 0xf)) { + /* If we found the first input with all components, and a corresponding prev output that uses + * less components */ + already_found_one = true; + } + + if (already_found_one) { + new_mask = io->usage_mask = (uint8_t)layout->usage_mask; + io->layout_location = layout->location; + io->array_id = layout->array_id; + + u_bit_scan_consecutive_range(&new_mask, &io->swizzle_offset, &io->num_components); + require_enhanced_layouts |= io->swizzle_offset > 0; + if (io->num_components == 1) + io->override_no_wm = true; + if (i_input < ctx->num_inputs - 1) { + already_found_one = (io[1].sid != layout->sid || io[1].array_id != layout->array_id); + } + } + } + } + } + ++io; + ++i_input; + } + return require_enhanced_layouts; +} + +static bool evaluate_layout_overlays(unsigned nio, struct vrend_shader_io *io, + const char *name_prefix, unsigned coord_replace) +{ + bool require_enhanced_layouts = 0; + int next_loc = 1; + + /* IO elements may be emitted for the same location but with + * non-overlapping swizzles, therefore, we modify the name of + * the variable to include the swizzle mask. + * + * Since TGSI also emits inputs that have no masks but are still at the + * same location, we also need to add an array ID. + */ + + for (unsigned i = 0; i < nio - 1; ++i) { + if ((io[i].name != TGSI_SEMANTIC_GENERIC && + io[i].name != TGSI_SEMANTIC_PATCH) || + io[i].usage_mask == 0xf || + io[i].layout_location > 0) + continue; + + for (unsigned j = i + 1; j < nio ; ++j) { + if ((io[j].name != TGSI_SEMANTIC_GENERIC && + io[j].name != TGSI_SEMANTIC_PATCH) || + io[j].usage_mask == 0xf || + io[j].layout_location > 0) + continue; + + /* Do the definition ranges overlap? */ + if (io[i].last < io[j].first || io[i].first > io[j].last) + continue; + + /* Overlapping ranges require explicite layouts and if they start at the + * same index thet location must be equal */ + if (io[i].first == io[j].first) { + io[j].layout_location = io[i].layout_location = next_loc++; + } else { + io[i].layout_location = next_loc++; + io[j].layout_location = next_loc++; + } + require_enhanced_layouts = true; + } + } + + rename_variables(nio, io, name_prefix, coord_replace); + + return require_enhanced_layouts; +} + + + +static +void renumber_io_arrays(unsigned nio, struct vrend_shader_io *io) +{ + int next_array_id = 1; + for (unsigned i = 0; i < nio; ++i) { + if (io[i].name != TGSI_SEMANTIC_GENERIC && + io[i].name != TGSI_SEMANTIC_PATCH) + continue; + if (io[i].array_id > 0) + io[i].array_id = next_array_id++; + } +} + +static void handle_io_arrays(struct dump_ctx *ctx) +{ + bool require_enhanced_layouts = false; + + /* If the guest sent real IO arrays then we declare them individually, + * and have to do some work to deal with overlapping values, regions and + * enhanced layouts */ + if (ctx->guest_sent_io_arrays) { + + /* Array ID numbering is not ordered accross shaders, so do + * some renumbering for generics and patches. */ + renumber_io_arrays(ctx->num_inputs, ctx->inputs); + renumber_io_arrays(ctx->num_outputs, ctx->outputs); + + } + + + /* In these shaders the inputs don't have the layout component information + * therefore, copy the info from the prev shaders output */ + if (ctx->prog_type == TGSI_PROCESSOR_GEOMETRY || + ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL || + ctx->prog_type == TGSI_PROCESSOR_TESS_EVAL) + require_enhanced_layouts |= apply_prev_layout(ctx); + + if (ctx->guest_sent_io_arrays) { + if (ctx->num_inputs > 0) + if (evaluate_layout_overlays(ctx->num_inputs, ctx->inputs, + get_stage_input_name_prefix(ctx, ctx->prog_type), + ctx->key->coord_replace)) { + require_enhanced_layouts = true; + } + + if (ctx->num_outputs > 0) + if (evaluate_layout_overlays(ctx->num_outputs, ctx->outputs, + get_stage_output_name_prefix(ctx->prog_type), 0)){ + require_enhanced_layouts = true; + } + + } else { + /* The guest didn't send real arrays, do we might have to add a big array + * for all generic and another ofr patch inputs */ + rewrite_io_ranged(ctx); + rewrite_components(ctx->num_inputs, ctx->inputs, + get_stage_input_name_prefix(ctx, ctx->prog_type), + ctx->key->coord_replace, true); + + rewrite_components(ctx->num_outputs, ctx->outputs, + get_stage_output_name_prefix(ctx->prog_type), 0, true); + } + + if (require_enhanced_layouts) { + ctx->shader_req_bits |= SHADER_REQ_ENHANCED_LAYOUTS; + ctx->shader_req_bits |= SHADER_REQ_SEPERATE_SHADER_OBJECTS; + } +} + + +static boolean +iter_instruction(struct tgsi_iterate_context *iter, + struct tgsi_full_instruction *inst) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + struct dest_info dinfo = { 0 }; + struct source_info sinfo = { 0 }; + const char *srcs[4]; + char dsts[3][255]; + char fp64_dsts[3][255]; + uint instno = ctx->instno++; + char writemask[6] = ""; + char src_swizzle0[10]; + + sinfo.svec4 = VEC4; + + if (ctx->prog_type == -1) + ctx->prog_type = iter->processor.Processor; + + if (instno == 0) { + handle_io_arrays(ctx); + + /* Vertex shader inputs are not send as arrays, but the access may still be + * indirect. so we have to deal with that */ + if (ctx->prog_type == TGSI_PROCESSOR_VERTEX && + ctx->info.indirect_files & (1 << TGSI_FILE_INPUT)) { + rewrite_vs_pos_array(ctx); + } + + emit_buf(ctx, "void main(void)\n{\n"); + if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + emit_color_select(ctx); + if (ctx->fs_uses_clipdist_input) + emit_fs_clipdistance_load(ctx); + } + if (ctx->so) + prepare_so_movs(ctx); + } + + if (!get_destination_info(ctx, inst, &dinfo, dsts, fp64_dsts, writemask)) + return false; + + if (!get_source_info(ctx, inst, &sinfo, ctx->src_bufs, src_swizzle0)) + return false; + + for (size_t i = 0; i < ARRAY_SIZE(srcs); ++i) + srcs[i] = ctx->src_bufs[i].buf; + + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_SQRT: + case TGSI_OPCODE_DSQRT: + emit_buff(ctx, "%s = sqrt(vec4(%s))%s;\n", dsts[0], srcs[0], writemask); + break; + case TGSI_OPCODE_LRP: + emit_buff(ctx, "%s = mix(vec4(%s), vec4(%s), vec4(%s))%s;\n", dsts[0], srcs[2], srcs[1], srcs[0], writemask); + break; + case TGSI_OPCODE_DP2: + emit_buff(ctx, "%s = %s(dot(vec2(%s), vec2(%s)));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1]); + break; + case TGSI_OPCODE_DP3: + emit_buff(ctx, "%s = %s(dot(vec3(%s), vec3(%s)));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1]); + break; + case TGSI_OPCODE_DP4: + emit_buff(ctx, "%s = %s(dot(vec4(%s), vec4(%s)));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1]); + break; + case TGSI_OPCODE_DPH: + emit_buff(ctx, "%s = %s(dot(vec4(vec3(%s), 1.0), vec4(%s)));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1]); + break; + case TGSI_OPCODE_MAX: + case TGSI_OPCODE_DMAX: + case TGSI_OPCODE_IMAX: + case TGSI_OPCODE_UMAX: + emit_buff(ctx, "%s = %s(%s(max(%s, %s))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_MIN: + case TGSI_OPCODE_DMIN: + case TGSI_OPCODE_IMIN: + case TGSI_OPCODE_UMIN: + emit_buff(ctx, "%s = %s(%s(min(%s, %s))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_ABS: + case TGSI_OPCODE_IABS: + case TGSI_OPCODE_DABS: + emit_op1("abs"); + break; + case TGSI_OPCODE_KILL_IF: + emit_buff(ctx, "if (any(lessThan(%s, vec4(0.0))))\ndiscard;\n", srcs[0]); + break; + case TGSI_OPCODE_IF: + case TGSI_OPCODE_UIF: + emit_buff(ctx, "if (any(bvec4(%s))) {\n", srcs[0]); + indent_buf(ctx); + break; + case TGSI_OPCODE_ELSE: + outdent_buf(ctx); + emit_buf(ctx, "} else {\n"); + indent_buf(ctx); + break; + case TGSI_OPCODE_ENDIF: + emit_buf(ctx, "}\n"); + outdent_buf(ctx); + break; + case TGSI_OPCODE_KILL: + emit_buff(ctx, "discard;\n"); + break; + case TGSI_OPCODE_DST: + emit_buff(ctx, "%s = vec4(1.0, %s.y * %s.y, %s.z, %s.w);\n", dsts[0], + srcs[0], srcs[1], srcs[0], srcs[1]); + break; + case TGSI_OPCODE_LIT: + emit_buff(ctx, "%s = %s(vec4(1.0, max(%s.x, 0.0), step(0.0, %s.x) * pow(max(0.0, %s.y), clamp(%s.w, -128.0, 128.0)), 1.0)%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[0], srcs[0], srcs[0], writemask); + break; + case TGSI_OPCODE_EX2: + emit_op1("exp2"); + break; + case TGSI_OPCODE_LG2: + emit_op1("log2"); + break; + case TGSI_OPCODE_EXP: + emit_buff(ctx, "%s = %s(vec4(pow(2.0, floor(%s.x)), %s.x - floor(%s.x), exp2(%s.x), 1.0)%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[0], srcs[0], srcs[0], writemask); + break; + case TGSI_OPCODE_LOG: + emit_buff(ctx, "%s = %s(vec4(floor(log2(%s.x)), %s.x / pow(2.0, floor(log2(%s.x))), log2(%s.x), 1.0)%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[0], srcs[0], srcs[0], writemask); + break; + case TGSI_OPCODE_COS: + emit_op1("cos"); + break; + case TGSI_OPCODE_SIN: + emit_op1("sin"); + break; + case TGSI_OPCODE_SCS: + emit_buff(ctx, "%s = %s(vec4(cos(%s.x), sin(%s.x), 0, 1)%s);\n", dsts[0], get_string(dinfo.dstconv), + srcs[0], srcs[0], writemask); + break; + case TGSI_OPCODE_DDX: + emit_op1("dFdx"); + break; + case TGSI_OPCODE_DDY: + emit_op1("dFdy"); + break; + case TGSI_OPCODE_DDX_FINE: + ctx->shader_req_bits |= SHADER_REQ_DERIVATIVE_CONTROL; + emit_op1("dFdxFine"); + break; + case TGSI_OPCODE_DDY_FINE: + ctx->shader_req_bits |= SHADER_REQ_DERIVATIVE_CONTROL; + emit_op1("dFdyFine"); + break; + case TGSI_OPCODE_RCP: + emit_buff(ctx, "%s = %s(1.0/(%s));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_DRCP: + emit_buff(ctx, "%s = %s(1.0LF/(%s));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_FLR: + emit_op1("floor"); + break; + case TGSI_OPCODE_ROUND: + emit_op1("round"); + break; + case TGSI_OPCODE_ISSG: + emit_op1("sign"); + break; + case TGSI_OPCODE_CEIL: + emit_op1("ceil"); + break; + case TGSI_OPCODE_FRC: + case TGSI_OPCODE_DFRAC: + emit_op1("fract"); + break; + case TGSI_OPCODE_TRUNC: + emit_op1("trunc"); + break; + case TGSI_OPCODE_SSG: + emit_op1("sign"); + break; + case TGSI_OPCODE_RSQ: + case TGSI_OPCODE_DRSQ: + emit_buff(ctx, "%s = %s(inversesqrt(%s.x));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_FBFETCH: + case TGSI_OPCODE_MOV: + emit_buff(ctx, "%s = %s(%s(%s%s));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], sinfo.override_no_wm[0] ? "" : writemask); + break; + case TGSI_OPCODE_ADD: + case TGSI_OPCODE_DADD: + emit_arit_op2("+"); + break; + case TGSI_OPCODE_UADD: + emit_buff(ctx, "%s = %s(%s(ivec4((uvec4(%s) + uvec4(%s))))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_SUB: + emit_arit_op2("-"); + break; + case TGSI_OPCODE_MUL: + case TGSI_OPCODE_DMUL: + emit_arit_op2("*"); + break; + case TGSI_OPCODE_DIV: + case TGSI_OPCODE_DDIV: + emit_arit_op2("/"); + break; + case TGSI_OPCODE_UMUL: + emit_buff(ctx, "%s = %s(%s((uvec4(%s) * uvec4(%s)))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_UMOD: + emit_buff(ctx, "%s = %s(%s((uvec4(%s) %% uvec4(%s)))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_IDIV: + emit_buff(ctx, "%s = %s(%s((ivec4(%s) / ivec4(%s)))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_UDIV: + emit_buff(ctx, "%s = %s(%s((uvec4(%s) / uvec4(%s)))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], writemask); + break; + case TGSI_OPCODE_ISHR: + case TGSI_OPCODE_USHR: + emit_arit_op2(">>"); + break; + case TGSI_OPCODE_SHL: + emit_arit_op2("<<"); + break; + case TGSI_OPCODE_MAD: + emit_buff(ctx, "%s = %s((%s * %s + %s)%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1], srcs[2], writemask); + break; + case TGSI_OPCODE_UMAD: + case TGSI_OPCODE_DMAD: + emit_buff(ctx, "%s = %s(%s((%s * %s + %s)%s));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], srcs[2], writemask); + break; + case TGSI_OPCODE_OR: + emit_arit_op2("|"); + break; + case TGSI_OPCODE_AND: + emit_arit_op2("&"); + break; + case TGSI_OPCODE_XOR: + emit_arit_op2("^"); + break; + case TGSI_OPCODE_MOD: + emit_arit_op2("%"); + break; + case TGSI_OPCODE_TEX: + case TGSI_OPCODE_TEX2: + case TGSI_OPCODE_TXB: + case TGSI_OPCODE_TXL: + case TGSI_OPCODE_TXB2: + case TGSI_OPCODE_TXL2: + case TGSI_OPCODE_TXD: + case TGSI_OPCODE_TXF: + case TGSI_OPCODE_TG4: + case TGSI_OPCODE_TXP: + case TGSI_OPCODE_LODQ: + translate_tex(ctx, inst, &sinfo, &dinfo, srcs, dsts[0], writemask); + break; + case TGSI_OPCODE_TXQ: + emit_txq(ctx, inst, sinfo.sreg_index, srcs, dsts[0], writemask); + break; + case TGSI_OPCODE_TXQS: + emit_txqs(ctx, inst, sinfo.sreg_index, srcs, dsts[0]); + break; + case TGSI_OPCODE_I2F: + emit_buff(ctx, "%s = %s(ivec4(%s)%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0], writemask); + break; + case TGSI_OPCODE_I2D: + emit_buff(ctx, "%s = %s(ivec4(%s));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_D2F: + emit_buff(ctx, "%s = %s(%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_U2F: + emit_buff(ctx, "%s = %s(uvec4(%s)%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0], writemask); + break; + case TGSI_OPCODE_U2D: + emit_buff(ctx, "%s = %s(uvec4(%s));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_F2I: + emit_buff(ctx, "%s = %s(%s(ivec4(%s))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], writemask); + break; + case TGSI_OPCODE_D2I: + emit_buff(ctx, "%s = %s(%s(%s(%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), get_string(dinfo.idstconv), srcs[0]); + break; + case TGSI_OPCODE_F2U: + emit_buff(ctx, "%s = %s(%s(uvec4(%s))%s);\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], writemask); + break; + case TGSI_OPCODE_D2U: + emit_buff(ctx, "%s = %s(%s(%s(%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), get_string(dinfo.udstconv), srcs[0]); + break; + case TGSI_OPCODE_F2D: + emit_buff(ctx, "%s = %s(%s(%s));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0]); + break; + case TGSI_OPCODE_NOT: + emit_buff(ctx, "%s = %s(uintBitsToFloat(~(uvec4(%s))));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_INEG: + emit_buff(ctx, "%s = %s(intBitsToFloat(-(ivec4(%s))));\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_DNEG: + emit_buff(ctx, "%s = %s(-%s);\n", dsts[0], get_string(dinfo.dstconv), srcs[0]); + break; + case TGSI_OPCODE_SEQ: + emit_compare("equal"); + break; + case TGSI_OPCODE_USEQ: + case TGSI_OPCODE_FSEQ: + case TGSI_OPCODE_DSEQ: + if (inst->Instruction.Opcode == TGSI_OPCODE_DSEQ) + strcpy(writemask, ".x"); + emit_ucompare("equal"); + break; + case TGSI_OPCODE_SLT: + emit_compare("lessThan"); + break; + case TGSI_OPCODE_ISLT: + case TGSI_OPCODE_USLT: + case TGSI_OPCODE_FSLT: + case TGSI_OPCODE_DSLT: + if (inst->Instruction.Opcode == TGSI_OPCODE_DSLT) + strcpy(writemask, ".x"); + emit_ucompare("lessThan"); + break; + case TGSI_OPCODE_SNE: + emit_compare("notEqual"); + break; + case TGSI_OPCODE_USNE: + case TGSI_OPCODE_FSNE: + case TGSI_OPCODE_DSNE: + if (inst->Instruction.Opcode == TGSI_OPCODE_DSNE) + strcpy(writemask, ".x"); + emit_ucompare("notEqual"); + break; + case TGSI_OPCODE_SGE: + emit_compare("greaterThanEqual"); + break; + case TGSI_OPCODE_ISGE: + case TGSI_OPCODE_USGE: + case TGSI_OPCODE_FSGE: + case TGSI_OPCODE_DSGE: + if (inst->Instruction.Opcode == TGSI_OPCODE_DSGE) + strcpy(writemask, ".x"); + emit_ucompare("greaterThanEqual"); + break; + case TGSI_OPCODE_POW: + emit_buff(ctx, "%s = %s(pow(%s, %s));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1]); + break; + case TGSI_OPCODE_CMP: + emit_buff(ctx, "%s = mix(%s, %s, greaterThanEqual(%s, vec4(0.0)))%s;\n", dsts[0], srcs[1], srcs[2], srcs[0], writemask); + break; + case TGSI_OPCODE_UCMP: + emit_buff(ctx, "%s = mix(%s, %s, notEqual(floatBitsToUint(%s), uvec4(0.0)))%s;\n", dsts[0], srcs[2], srcs[1], srcs[0], writemask); + break; + case TGSI_OPCODE_END: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX) { + handle_vertex_proc_exit(ctx); + } else if (iter->processor.Processor == TGSI_PROCESSOR_TESS_CTRL) { + emit_clip_dist_movs(ctx); + } else if (iter->processor.Processor == TGSI_PROCESSOR_TESS_EVAL) { + if (ctx->so && !ctx->key->gs_present) + emit_so_movs(ctx); + emit_clip_dist_movs(ctx); + if (!ctx->key->gs_present) { + emit_prescale(ctx); + } + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + handle_fragment_proc_exit(ctx); + } + emit_buf(ctx, "}\n"); + break; + case TGSI_OPCODE_RET: + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX) { + handle_vertex_proc_exit(ctx); + } else if (iter->processor.Processor == TGSI_PROCESSOR_FRAGMENT) { + handle_fragment_proc_exit(ctx); + } + emit_buf(ctx, "return;\n"); + break; + case TGSI_OPCODE_ARL: + emit_buff(ctx, "%s = int(floor(%s)%s);\n", dsts[0], srcs[0], writemask); + break; + case TGSI_OPCODE_UARL: + emit_buff(ctx, "%s = int(%s);\n", dsts[0], srcs[0]); + break; + case TGSI_OPCODE_XPD: + emit_buff(ctx, "%s = %s(cross(vec3(%s), vec3(%s)));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1]); + break; + case TGSI_OPCODE_BGNLOOP: + emit_buf(ctx, "do {\n"); + indent_buf(ctx); + break; + case TGSI_OPCODE_ENDLOOP: + outdent_buf(ctx); + emit_buf(ctx, "} while(true);\n"); + break; + case TGSI_OPCODE_BRK: + emit_buf(ctx, "break;\n"); + break; + case TGSI_OPCODE_EMIT: { + struct immed *imd = &ctx->imm[(inst->Src[0].Register.Index)]; + if (ctx->so && ctx->key->gs_present) + emit_so_movs(ctx); + emit_clip_dist_movs(ctx); + emit_prescale(ctx); + if (imd->val[inst->Src[0].Register.SwizzleX].ui > 0) { + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + emit_buff(ctx, "EmitStreamVertex(%d);\n", imd->val[inst->Src[0].Register.SwizzleX].ui); + } else + emit_buf(ctx, "EmitVertex();\n"); + break; + } + case TGSI_OPCODE_ENDPRIM: { + struct immed *imd = &ctx->imm[(inst->Src[0].Register.Index)]; + if (imd->val[inst->Src[0].Register.SwizzleX].ui > 0) { + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + emit_buff(ctx, "EndStreamPrimitive(%d);\n", imd->val[inst->Src[0].Register.SwizzleX].ui); + } else + emit_buf(ctx, "EndPrimitive();\n"); + break; + } + case TGSI_OPCODE_INTERP_CENTROID: + emit_buff(ctx, "%s = %s(%s(vec4(interpolateAtCentroid(%s)%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], src_swizzle0); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_INTERP_SAMPLE: + emit_buff(ctx, "%s = %s(%s(vec4(interpolateAtSample(%s, %s.x)%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], src_swizzle0); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_INTERP_OFFSET: + emit_buff(ctx, "%s = %s(%s(vec4(interpolateAtOffset(%s, %s.xy)%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], src_swizzle0); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_UMUL_HI: + emit_buff(ctx, "umulExtended(%s, %s, umul_temp, mul_utemp);\n", srcs[0], srcs[1]); + emit_buff(ctx, "%s = %s(%s(umul_temp%s));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), writemask); + ctx->write_mul_utemp = true; + break; + case TGSI_OPCODE_IMUL_HI: + emit_buff(ctx, "imulExtended(%s, %s, imul_temp, mul_itemp);\n", srcs[0], srcs[1]); + emit_buff(ctx, "%s = %s(%s(imul_temp%s));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), writemask); + ctx->write_mul_itemp = true; + break; + + case TGSI_OPCODE_IBFE: + emit_buff(ctx, "%s = %s(%s(bitfieldExtract(%s, int(%s.x), int(%s.x))));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], srcs[2]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_UBFE: + emit_buff(ctx, "%s = %s(%s(bitfieldExtract(%s, int(%s.x), int(%s.x))));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0], srcs[1], srcs[2]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_BFI: + emit_buff(ctx, "%s = %s(uintBitsToFloat(bitfieldInsert(%s, %s, int(%s), int(%s))));\n", dsts[0], get_string(dinfo.dstconv), srcs[0], srcs[1], srcs[2], srcs[3]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_BREV: + emit_buff(ctx, "%s = %s(%s(bitfieldReverse(%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_POPC: + emit_buff(ctx, "%s = %s(%s(bitCount(%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_LSB: + emit_buff(ctx, "%s = %s(%s(findLSB(%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_IMSB: + case TGSI_OPCODE_UMSB: + emit_buff(ctx, "%s = %s(%s(findMSB(%s)));\n", dsts[0], get_string(dinfo.dstconv), get_string(dinfo.dtypeprefix), srcs[0]); + ctx->shader_req_bits |= SHADER_REQ_GPU_SHADER5; + break; + case TGSI_OPCODE_BARRIER: + emit_buf(ctx, "barrier();\n"); + break; + case TGSI_OPCODE_MEMBAR: { + struct immed *imd = &ctx->imm[(inst->Src[0].Register.Index)]; + uint32_t val = imd->val[inst->Src[0].Register.SwizzleX].ui; + uint32_t all_val = (TGSI_MEMBAR_SHADER_BUFFER | + TGSI_MEMBAR_ATOMIC_BUFFER | + TGSI_MEMBAR_SHADER_IMAGE | + TGSI_MEMBAR_SHARED); + + if (val & TGSI_MEMBAR_THREAD_GROUP) { + emit_buf(ctx, "groupMemoryBarrier();\n"); + } else { + if ((val & all_val) == all_val) { + emit_buf(ctx, "memoryBarrier();\n"); + ctx->shader_req_bits |= SHADER_REQ_IMAGE_LOAD_STORE; + } else { + if (val & TGSI_MEMBAR_SHADER_BUFFER) { + emit_buf(ctx, "memoryBarrierBuffer();\n"); + } + if (val & TGSI_MEMBAR_ATOMIC_BUFFER) { + emit_buf(ctx, "memoryBarrierAtomic();\n"); + } + if (val & TGSI_MEMBAR_SHADER_IMAGE) { + emit_buf(ctx, "memoryBarrierImage();\n"); + } + if (val & TGSI_MEMBAR_SHARED) { + emit_buf(ctx, "memoryBarrierShared();\n"); + } + } + } + break; + } + case TGSI_OPCODE_STORE: + if (!rewrite_1d_image_coordinate(ctx->src_bufs + 1, inst)) + return false; + srcs[1] = ctx->src_bufs[1].buf; + translate_store(ctx, inst, &sinfo, srcs, dsts[0]); + break; + case TGSI_OPCODE_LOAD: + if (!rewrite_1d_image_coordinate(ctx->src_bufs + 1, inst)) + return false; + srcs[1] = ctx->src_bufs[1].buf; + translate_load(ctx, inst, &sinfo, &dinfo, srcs, dsts[0], writemask); + break; + case TGSI_OPCODE_ATOMUADD: + case TGSI_OPCODE_ATOMXCHG: + case TGSI_OPCODE_ATOMCAS: + case TGSI_OPCODE_ATOMAND: + case TGSI_OPCODE_ATOMOR: + case TGSI_OPCODE_ATOMXOR: + case TGSI_OPCODE_ATOMUMIN: + case TGSI_OPCODE_ATOMUMAX: + case TGSI_OPCODE_ATOMIMIN: + case TGSI_OPCODE_ATOMIMAX: + if (!rewrite_1d_image_coordinate(ctx->src_bufs + 1, inst)) + return false; + srcs[1] = ctx->src_bufs[1].buf; + translate_atomic(ctx, inst, &sinfo, srcs, dsts[0]); + break; + case TGSI_OPCODE_RESQ: + translate_resq(ctx, inst, srcs, dsts[0], writemask); + break; + case TGSI_OPCODE_CLOCK: + ctx->shader_req_bits |= SHADER_REQ_SHADER_CLOCK; + emit_buff(ctx, "%s = uintBitsToFloat(clock2x32ARB());\n", dsts[0]); + break; + } + + for (uint32_t i = 0; i < 1; i++) { + enum tgsi_opcode_type dtype = tgsi_opcode_infer_dst_type(inst->Instruction.Opcode); + if (dtype == TGSI_TYPE_DOUBLE) { + emit_buff(ctx, "%s = uintBitsToFloat(unpackDouble2x32(%s));\n", fp64_dsts[0], dsts[0]); + } + } + if (inst->Instruction.Saturate) { + emit_buff(ctx, "%s = clamp(%s, 0.0, 1.0);\n", dsts[0], dsts[0]); + } + + if (strbuf_get_error(&ctx->glsl_main)) + return false; + return true; +} + +static boolean +prolog(struct tgsi_iterate_context *iter) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + + if (ctx->prog_type == -1) + ctx->prog_type = iter->processor.Processor; + + if (iter->processor.Processor == TGSI_PROCESSOR_VERTEX && + ctx->key->gs_present) + require_glsl_ver(ctx, 150); + + return true; +} + +static void emit_ext(struct dump_ctx *ctx, const char *name, + const char *verb) +{ + emit_ver_extf(ctx, "#extension GL_%s : %s\n", name, verb); +} + +static void emit_header(struct dump_ctx *ctx) +{ + emit_ver_extf(ctx, "#version %d es\n", ctx->cfg->glsl_version); + + if ((ctx->shader_req_bits & SHADER_REQ_CLIP_DISTANCE)|| + (ctx->num_clip_dist == 0 && ctx->key->clip_plane_enable)) { + emit_ext(ctx, "EXT_clip_cull_distance", "require"); + } + + if (ctx->shader_req_bits & SHADER_REQ_SAMPLER_MS) + emit_ext(ctx, "OES_texture_storage_multisample_2d_array", "require"); + + if (ctx->shader_req_bits & SHADER_REQ_CONSERVATIVE_DEPTH) + emit_ext(ctx, "EXT_conservative_depth", "require"); + + if (ctx->prog_type == TGSI_PROCESSOR_FRAGMENT) { + if (ctx->shader_req_bits & SHADER_REQ_FBFETCH) + emit_ext(ctx, "EXT_shader_framebuffer_fetch", "require"); + } + + if (ctx->shader_req_bits & SHADER_REQ_VIEWPORT_IDX) + emit_ext(ctx, "OES_viewport_array", "require"); + + if (ctx->prog_type == TGSI_PROCESSOR_GEOMETRY) { + emit_ext(ctx, "EXT_geometry_shader", "require"); + if (ctx->shader_req_bits & SHADER_REQ_PSIZE) + emit_ext(ctx, "OES_geometry_point_size", "enable"); + } + + if (ctx->shader_req_bits & SHADER_REQ_NV_IMAGE_FORMATS) + emit_ext(ctx, "NV_image_formats", "require"); + + if ((ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL || + ctx->prog_type == TGSI_PROCESSOR_TESS_EVAL)) { + if (ctx->cfg->glsl_version < 320) + emit_ext(ctx, "OES_tessellation_shader", "require"); + emit_ext(ctx, "OES_tessellation_point_size", "enable"); + } + + if (ctx->cfg->glsl_version < 320) { + if (ctx->shader_req_bits & SHADER_REQ_SAMPLE_SHADING) + emit_ext(ctx, "OES_sample_variables", "require"); + if (ctx->shader_req_bits & SHADER_REQ_GPU_SHADER5) { + emit_ext(ctx, "OES_gpu_shader5", "require"); + emit_ext(ctx, "OES_shader_multisample_interpolation", + "require"); + } + if (ctx->shader_req_bits & SHADER_REQ_CUBE_ARRAY) + emit_ext(ctx, "OES_texture_cube_map_array", "require"); + if (ctx->shader_req_bits & SHADER_REQ_LAYER) + emit_ext(ctx, "EXT_geometry_shader", "require"); + if (ctx->shader_req_bits & SHADER_REQ_IMAGE_ATOMIC) + emit_ext(ctx, "OES_shader_image_atomic", "require"); + } + + if (logiop_require_inout(ctx->key)) { + if (ctx->key->fs_logicop_emulate_coherent) + emit_ext(ctx, "EXT_shader_framebuffer_fetch", "require"); + else + emit_ext(ctx, "EXT_shader_framebuffer_fetch_non_coherent", "require"); + + } + + if (ctx->shader_req_bits & SHADER_REQ_LODQ) + emit_ext(ctx, "EXT_texture_query_lod", "require"); + + emit_hdr(ctx, "precision highp float;\n"); + emit_hdr(ctx, "precision highp int;\n"); +} + +char vrend_shader_samplerreturnconv(enum tgsi_return_type type) +{ + switch (type) { + case TGSI_RETURN_TYPE_SINT: + return 'i'; + case TGSI_RETURN_TYPE_UINT: + return 'u'; + default: + return ' '; + } +} + +const char *vrend_shader_samplertypeconv(int sampler_type) +{ + switch (sampler_type) { + case TGSI_TEXTURE_BUFFER: return "Buffer"; + case TGSI_TEXTURE_1D: + /* fallthrough */ + case TGSI_TEXTURE_2D: return "2D"; + case TGSI_TEXTURE_3D: return "3D"; + case TGSI_TEXTURE_CUBE: return "Cube"; + case TGSI_TEXTURE_RECT: return "2D"; + case TGSI_TEXTURE_SHADOW1D: + /* fallthrough */ + case TGSI_TEXTURE_SHADOW2D: return "2DShadow"; + case TGSI_TEXTURE_SHADOWRECT: + return "2DShadow"; + case TGSI_TEXTURE_1D_ARRAY: + /* fallthrough */ + case TGSI_TEXTURE_2D_ARRAY: return "2DArray"; + case TGSI_TEXTURE_SHADOW1D_ARRAY: + /* fallthrough */ + case TGSI_TEXTURE_SHADOW2D_ARRAY: return "2DArrayShadow"; + case TGSI_TEXTURE_SHADOWCUBE: return "CubeShadow"; + case TGSI_TEXTURE_CUBE_ARRAY: return "CubeArray"; + case TGSI_TEXTURE_SHADOWCUBE_ARRAY: return "CubeArrayShadow"; + case TGSI_TEXTURE_2D_MSAA: return "2DMS"; + case TGSI_TEXTURE_2D_ARRAY_MSAA: return "2DMSArray"; + default: return NULL; + } +} + +static const char *get_interp_string(struct vrend_shader_cfg *cfg, int interpolate, bool flatshade) +{ + switch (interpolate) { + case TGSI_INTERPOLATE_LINEAR: + return ""; + case TGSI_INTERPOLATE_PERSPECTIVE: + return "smooth "; + case TGSI_INTERPOLATE_CONSTANT: + return "flat "; + case TGSI_INTERPOLATE_COLOR: + if (flatshade) + return "flat "; + /* fallthrough */ + default: + return NULL; + } +} + +static const char *get_aux_string(unsigned location) +{ + switch (location) { + case TGSI_INTERPOLATE_LOC_CENTER: + default: + return ""; + case TGSI_INTERPOLATE_LOC_CENTROID: + return "centroid "; + case TGSI_INTERPOLATE_LOC_SAMPLE: + return "sample "; + } +} + +static void emit_sampler_decl(struct dump_ctx *ctx, + uint32_t i, uint32_t range, + const struct vrend_shader_sampler *sampler) +{ + char ptc; + bool is_shad; + const char *sname, *precision, *stc; + + sname = tgsi_proc_to_prefix(ctx->prog_type); + + precision = "highp"; + + ptc = vrend_shader_samplerreturnconv(sampler->tgsi_sampler_return); + stc = vrend_shader_samplertypeconv(sampler->tgsi_sampler_type); + is_shad = samplertype_is_shadow(sampler->tgsi_sampler_type); + + if (range) + emit_hdrf(ctx, "uniform %s %csampler%s %ssamp%d[%d];\n", precision, ptc, stc, sname, i, range); + else + emit_hdrf(ctx, "uniform %s %csampler%s %ssamp%d;\n", precision, ptc, stc, sname, i); + + if (is_shad) { + emit_hdrf(ctx, "uniform %s vec4 %sshadmask%d;\n", precision, sname, i); + emit_hdrf(ctx, "uniform %s vec4 %sshadadd%d;\n", precision, sname, i); + ctx->shadow_samp_mask |= (1 << i); + } +} + +const char *get_internalformat_string(int virgl_format, enum tgsi_return_type *stype) +{ + switch (virgl_format) { + case PIPE_FORMAT_R11G11B10_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "r11f_g11f_b10f"; + case PIPE_FORMAT_R10G10B10A2_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "rgb10_a2"; + case PIPE_FORMAT_R10G10B10A2_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rgb10_a2ui"; + case PIPE_FORMAT_R8_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "r8"; + case PIPE_FORMAT_R8_SNORM: + *stype = TGSI_RETURN_TYPE_SNORM; + return "r8_snorm"; + case PIPE_FORMAT_R8_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "r8ui"; + case PIPE_FORMAT_R8_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "r8i"; + case PIPE_FORMAT_R8G8_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "rg8"; + case PIPE_FORMAT_R8G8_SNORM: + *stype = TGSI_RETURN_TYPE_SNORM; + return "rg8_snorm"; + case PIPE_FORMAT_R8G8_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rg8ui"; + case PIPE_FORMAT_R8G8_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "rg8i"; + case PIPE_FORMAT_R8G8B8A8_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "rgba8"; + case PIPE_FORMAT_R8G8B8A8_SNORM: + *stype = TGSI_RETURN_TYPE_SNORM; + return "rgba8_snorm"; + case PIPE_FORMAT_R8G8B8A8_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rgba8ui"; + case PIPE_FORMAT_R8G8B8A8_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "rgba8i"; + case PIPE_FORMAT_R16_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "r16"; + case PIPE_FORMAT_R16_SNORM: + *stype = TGSI_RETURN_TYPE_SNORM; + return "r16_snorm"; + case PIPE_FORMAT_R16_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "r16ui"; + case PIPE_FORMAT_R16_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "r16i"; + case PIPE_FORMAT_R16_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "r16f"; + case PIPE_FORMAT_R16G16_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "rg16"; + case PIPE_FORMAT_R16G16_SNORM: + *stype = TGSI_RETURN_TYPE_SNORM; + return "rg16_snorm"; + case PIPE_FORMAT_R16G16_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rg16ui"; + case PIPE_FORMAT_R16G16_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "rg16i"; + case PIPE_FORMAT_R16G16_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "rg16f"; + case PIPE_FORMAT_R16G16B16A16_UNORM: + *stype = TGSI_RETURN_TYPE_UNORM; + return "rgba16"; + case PIPE_FORMAT_R16G16B16A16_SNORM: + *stype = TGSI_RETURN_TYPE_SNORM; + return "rgba16_snorm"; + case PIPE_FORMAT_R16G16B16A16_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "rgba16f"; + case PIPE_FORMAT_R32_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "r32f"; + case PIPE_FORMAT_R32_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "r32ui"; + case PIPE_FORMAT_R32_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "r32i"; + case PIPE_FORMAT_R32G32_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "rg32f"; + case PIPE_FORMAT_R32G32_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rg32ui"; + case PIPE_FORMAT_R32G32_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "rg32i"; + case PIPE_FORMAT_R32G32B32A32_FLOAT: + *stype = TGSI_RETURN_TYPE_FLOAT; + return "rgba32f"; + case PIPE_FORMAT_R32G32B32A32_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rgba32ui"; + case PIPE_FORMAT_R16G16B16A16_UINT: + *stype = TGSI_RETURN_TYPE_UINT; + return "rgba16ui"; + case PIPE_FORMAT_R16G16B16A16_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "rgba16i"; + case PIPE_FORMAT_R32G32B32A32_SINT: + *stype = TGSI_RETURN_TYPE_SINT; + return "rgba32i"; + case PIPE_FORMAT_NONE: + *stype = TGSI_RETURN_TYPE_UNORM; + return ""; + default: + *stype = TGSI_RETURN_TYPE_UNORM; + return ""; + } +} + +static void emit_image_decl(struct dump_ctx *ctx, + uint32_t i, uint32_t range, + const struct vrend_shader_image *image) +{ + char ptc; + const char *sname, *stc, *formatstr; + enum tgsi_return_type itype; + const char *volatile_str = image->vflag ? "volatile " : ""; + const char *precision = "highp "; + const char *access = ""; + formatstr = get_internalformat_string(image->decl.Format, &itype); + ptc = vrend_shader_samplerreturnconv(itype); + sname = tgsi_proc_to_prefix(ctx->prog_type); + stc = vrend_shader_samplertypeconv(image->decl.Resource); + + if (!image->decl.Writable) + access = "readonly "; + else if (!image->decl.Format || + ((image->decl.Format != PIPE_FORMAT_R32_FLOAT) && + (image->decl.Format != PIPE_FORMAT_R32_SINT) && + (image->decl.Format != PIPE_FORMAT_R32_UINT))) + access = "writeonly "; + + emit_hdrf(ctx, "layout(binding=%d%s%s) ", + i, formatstr[0] != '\0' ? ", " : ", rgba32f", formatstr); + + if (range) + emit_hdrf(ctx, "%s%suniform %s%cimage%s %simg%d[%d];\n", + access, volatile_str, precision, ptc, stc, sname, i, range); + else + emit_hdrf(ctx, "%s%suniform %s%cimage%s %simg%d;\n", + access, volatile_str, precision, ptc, stc, sname, i); +} + +static void emit_ios_common(struct dump_ctx *ctx) +{ + uint i; + const char *sname = tgsi_proc_to_prefix(ctx->prog_type); + + for (i = 0; i < ctx->num_temp_ranges; i++) { + emit_hdrf(ctx, "vec4 temp%d[%d];\n", ctx->temp_ranges[i].first, ctx->temp_ranges[i].last - ctx->temp_ranges[i].first + 1); + } + + if (ctx->write_mul_utemp) { + emit_hdr(ctx, "uvec4 mul_utemp;\n"); + emit_hdr(ctx, "uvec4 umul_temp;\n"); + } + + if (ctx->write_mul_itemp) { + emit_hdr(ctx, "ivec4 mul_itemp;\n"); + emit_hdr(ctx, "ivec4 imul_temp;\n"); + } + + if (ctx->ssbo_used_mask || ctx->has_file_memory) { + emit_hdr(ctx, "uint ssbo_addr_temp;\n"); + } + + if (ctx->shader_req_bits & SHADER_REQ_FP64) { + emit_hdr(ctx, "dvec2 fp64_dst[3];\n"); + emit_hdr(ctx, "dvec2 fp64_src[4];\n"); + } + + for (i = 0; i < ctx->num_address; i++) { + emit_hdrf(ctx, "int addr%d;\n", i); + } + if (ctx->num_consts) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + emit_hdrf(ctx, "uniform uvec4 %sconst0[%d];\n", cname, ctx->num_consts); + } + + if (ctx->ubo_used_mask) { + const char *cname = tgsi_proc_to_prefix(ctx->prog_type); + + if (ctx->info.dimension_indirect_files & (1 << TGSI_FILE_CONSTANT)) { + require_glsl_ver(ctx, 150); + int first = ffs(ctx->ubo_used_mask) - 1; + unsigned num_ubo = util_bitcount(ctx->ubo_used_mask); + emit_hdrf(ctx, "uniform %subo { vec4 ubocontents[%d]; } %suboarr[%d];\n", cname, ctx->ubo_sizes[first], cname, num_ubo); + } else { + unsigned mask = ctx->ubo_used_mask; + while (mask) { + uint32_t j = u_bit_scan(&mask); + emit_hdrf(ctx, "uniform %subo%d { vec4 %subo%dcontents[%d]; };\n", cname, j, cname, j, ctx->ubo_sizes[i]); + } + } + } + + if (ctx->info.indirect_files & (1 << TGSI_FILE_SAMPLER)) { + for (i = 0; i < ctx->num_sampler_arrays; i++) { + uint32_t first = ctx->sampler_arrays[i].first; + uint32_t range = ctx->sampler_arrays[i].array_size; + emit_sampler_decl(ctx, first, range, ctx->samplers + first); + } + } else { + uint nsamp = util_last_bit(ctx->samplers_used); + for (i = 0; i < nsamp; i++) { + + if ((ctx->samplers_used & (1 << i)) == 0) + continue; + + emit_sampler_decl(ctx, i, 0, ctx->samplers + i); + } + } + + if (ctx->info.indirect_files & (1 << TGSI_FILE_IMAGE)) { + for (i = 0; i < ctx->num_image_arrays; i++) { + uint32_t first = ctx->image_arrays[i].first; + uint32_t range = ctx->image_arrays[i].array_size; + emit_image_decl(ctx, first, range, ctx->images + first); + } + } else { + uint32_t mask = ctx->images_used_mask; + while (mask) { + i = u_bit_scan(&mask); + emit_image_decl(ctx, i, 0, ctx->images + i); + } + } + + for (i = 0; i < ctx->num_abo; i++){ + if (ctx->abo_sizes[i] > 1) + emit_hdrf(ctx, "layout (binding = %d, offset = %d) uniform atomic_uint ac%d[%d];\n", ctx->abo_idx[i], ctx->abo_offsets[i] * 4, i, ctx->abo_sizes[i]); + else + emit_hdrf(ctx, "layout (binding = %d, offset = %d) uniform atomic_uint ac%d;\n", ctx->abo_idx[i], ctx->abo_offsets[i] * 4, i); + } + + if (ctx->info.indirect_files & (1 << TGSI_FILE_BUFFER)) { + uint32_t mask = ctx->ssbo_used_mask; + while (mask) { + int start, count; + u_bit_scan_consecutive_range(&mask, &start, &count); + const char *atomic = (ctx->ssbo_atomic_mask & (1 << start)) ? "atomic" : ""; + emit_hdrf(ctx, "layout (binding = %d, std430) buffer %sssbo%d { uint %sssbocontents%d[]; } %sssboarr%s[%d];\n", start, sname, start, sname, start, sname, atomic, count); + } + } else { + uint32_t mask = ctx->ssbo_used_mask; + while (mask) { + uint32_t id = u_bit_scan(&mask); + enum vrend_type_qualifier type = (ctx->ssbo_integer_mask & (1 << id)) ? INT : UINT; + char *coherent = ctx->ssbo_memory_qualifier[id] == TGSI_MEMORY_COHERENT ? "coherent" : ""; + emit_hdrf(ctx, "layout (binding = %d, std430) %s buffer %sssbo%d { %s %sssbocontents%d[]; };\n", id, coherent, sname, id, + get_string(type), sname, id); + } + } + +} + +static void emit_ios_streamout(struct dump_ctx *ctx) +{ + if (ctx->so) { + char outtype[6] = ""; + for (uint i = 0; i < ctx->so->num_outputs; i++) { + if (!ctx->write_so_outputs[i]) + continue; + if (ctx->so->output[i].num_components == 1) + snprintf(outtype, 6, "float"); + else + snprintf(outtype, 6, "vec%d", ctx->so->output[i].num_components); + + if (ctx->so->output[i].stream && ctx->prog_type == TGSI_PROCESSOR_GEOMETRY) + emit_hdrf(ctx, "layout (stream=%d) out %s tfout%d;\n", ctx->so->output[i].stream, outtype, i); + else { + const struct vrend_shader_io *output = get_io_slot(&ctx->outputs[0], ctx->num_outputs, + ctx->so->output[i].register_index); + if (ctx->so->output[i].need_temp || output->name == TGSI_SEMANTIC_CLIPDIST || + output->glsl_predefined_no_emit) { + + if (ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL) + emit_hdrf(ctx, "out %s tfout%d[];\n", outtype, i); + else + emit_hdrf(ctx, "out %s tfout%d;\n", outtype, i); + } + } + } + } +} + +static inline void emit_winsys_correction(struct dump_ctx *ctx) +{ + emit_hdr(ctx, "uniform float winsys_adjust_y;\n"); +} + +static void emit_ios_indirect_generics_output(struct dump_ctx *ctx, const char *postfix) +{ + if (ctx->generic_output_range.used) { + int size = ctx->generic_output_range.io.last - ctx->generic_output_range.io.sid + 1; + if (prefer_generic_io_block(ctx, io_out)) { + char blockname[64]; + const char *stage_prefix = get_stage_output_name_prefix(ctx->prog_type); + get_blockname(blockname, stage_prefix, &ctx->generic_output_range.io); + + char blockvarame[64]; + get_blockvarname(blockvarame, stage_prefix, &ctx->generic_output_range.io, postfix); + + emit_hdrf(ctx, "out %s {\n vec4 %s[%d]; \n} %s;\n", blockname, + ctx->generic_output_range.io.glsl_name, size, blockvarame); + } else + emit_hdrf(ctx, "out vec4 %s%s[%d];\n", + ctx->generic_output_range.io.glsl_name, + postfix, + size); + } +} + +static void emit_ios_indirect_generics_input(struct dump_ctx *ctx, const char *postfix) +{ + if (ctx->generic_input_range.used) { + int size = ctx->generic_input_range.io.last - ctx->generic_input_range.io.sid + 1; + assert(size < 256 && size >= 0); + + if (prefer_generic_io_block(ctx, io_in)) { + char blockname[64]; + char blockvarame[64]; + const char *stage_prefix = get_stage_input_name_prefix(ctx, ctx->prog_type); + + get_blockname(blockname, stage_prefix, &ctx->generic_input_range.io); + get_blockvarname(blockvarame, stage_prefix, &ctx->generic_input_range.io, + postfix); + + emit_hdrf(ctx, "in %s {\n vec4 %s[%d]; \n} %s;\n", + blockname, ctx->generic_input_range.io.glsl_name, + size, blockvarame); + } else + emit_hdrf(ctx, "in vec4 %s%s[%d];\n", + ctx->generic_input_range.io.glsl_name, + postfix, + size); + } +} + +static void +emit_ios_generic(struct dump_ctx *ctx, enum io_type iot, const char *prefix, + const struct vrend_shader_io *io, const char *inout, + const char *postfix) +{ + const char type[4][6] = {"float", " vec2", " vec3", " vec4"}; + const char *t = " vec4"; + + char layout[128] = ""; + + if (io->layout_location > 0) { + /* we need to define a layout here because interleaved arrays might be emited */ + if (io->swizzle_offset) + snprintf(layout, sizeof(layout), "layout(location = %d, component = %d)\n", + io->layout_location - 1, io->swizzle_offset); + else + snprintf(layout, sizeof(layout), "layout(location = %d)\n", io->layout_location - 1); + } + + if (io->usage_mask != 0xf && io->name == TGSI_SEMANTIC_GENERIC) + t = type[io->num_components - 1]; + + if (io->first == io->last) { + emit_hdr(ctx, layout); + /* ugly leave spaces to patch interp in later */ + emit_hdrf(ctx, "%s%s\n%s %s %s %s%s;\n", + io->precise ? "precise" : "", + io->invariant ? "invariant" : "", + prefix, + inout, + t, + io->glsl_name, + postfix); + + if (io->name == TGSI_SEMANTIC_GENERIC) { + if (iot == io_in) + ctx->generic_inputs_emitted_mask |= 1 << io->sid; + else + ctx->generic_outputs_emitted_mask |= 1 << io->sid; + } + + } else { + if (prefer_generic_io_block(ctx, iot)) { + const char *stage_prefix = iot == io_in ? get_stage_input_name_prefix(ctx, ctx->prog_type): + get_stage_output_name_prefix(ctx->prog_type); + + char blockname[64]; + get_blockname(blockname, stage_prefix, io); + + char blockvarame[64]; + get_blockvarname(blockvarame, stage_prefix, io, postfix); + + emit_hdrf(ctx, "%s %s {\n", inout, blockname); + emit_hdr(ctx, layout); + emit_hdrf(ctx, "%s%s\n%s %s %s[%d]; \n} %s;\n", + io->precise ? "precise" : "", + io->invariant ? "invariant" : "", + prefix, + t, + io->glsl_name, + io->last - io->first +1, + blockvarame); + } else { + emit_hdr(ctx, layout); + emit_hdrf(ctx, "%s%s\n%s %s %s %s%s[%d];\n", + io->precise ? "precise" : "", + io->invariant ? "invariant" : "", + prefix, + inout, + t, + io->glsl_name, + postfix, + io->last - io->first +1); + + } + } +} + +typedef bool (*can_emit_generic_callback)(const struct vrend_shader_io *io); + +static void +emit_ios_generic_outputs(struct dump_ctx *ctx, + const can_emit_generic_callback can_emit_generic) +{ + uint32_t i; + uint64_t fc_emitted = 0; + uint64_t bc_emitted = 0; + + for (i = 0; i < ctx->num_outputs; i++) { + + if (!ctx->outputs[i].glsl_predefined_no_emit) { + /* GS stream outputs are handled separately */ + if (!can_emit_generic(&ctx->outputs[i])) + continue; + + const char *prefix = ""; + if (ctx->outputs[i].name == TGSI_SEMANTIC_GENERIC || + ctx->outputs[i].name == TGSI_SEMANTIC_COLOR || + ctx->outputs[i].name == TGSI_SEMANTIC_BCOLOR) { + ctx->num_interps++; + /* ugly leave spaces to patch interp in later */ + prefix = INTERP_PREFIX; + } + + if (ctx->outputs[i].name == TGSI_SEMANTIC_COLOR) { + ctx->front_back_color_emitted_flags[ctx->outputs[i].sid] |= FRONT_COLOR_EMITTED; + fc_emitted |= 1ull << ctx->outputs[i].sid; + } + + if (ctx->outputs[i].name == TGSI_SEMANTIC_BCOLOR) { + ctx->front_back_color_emitted_flags[ctx->outputs[i].sid] |= BACK_COLOR_EMITTED; + bc_emitted |= 1ull << ctx->outputs[i].sid; + } + + emit_ios_generic(ctx, io_out, prefix, &ctx->outputs[i], + ctx->outputs[i].fbfetch_used ? "inout" : "out", ""); + } else if (ctx->outputs[i].invariant || ctx->outputs[i].precise) { + emit_hdrf(ctx, "%s%s;\n", + ctx->outputs[i].precise ? "precise " : + (ctx->outputs[i].invariant ? "invariant " : ""), + ctx->outputs[i].glsl_name); + } + } + + /* If a back color emitted without a corresponding front color, then + * we have to force two side coloring, because the FS shader might expect + * a front color too. */ + if (bc_emitted & ~fc_emitted) + ctx->force_color_two_side = 1; +} + +static void +emit_ios_patch(struct dump_ctx *ctx, const char *prefix, const struct vrend_shader_io *io, + const char *inout, int size) +{ + const char type[4][6] = {"float", " vec2", " vec3", " vec4"}; + const char *t = " vec4"; + + if (io->layout_location > 0) { + /* we need to define a layout here because interleaved arrays might be emited */ + if (io->swizzle_offset) + emit_hdrf(ctx, "layout(location = %d, component = %d)\n", + io->layout_location - 1, io->swizzle_offset); + else + emit_hdrf(ctx, "layout(location = %d)\n", io->layout_location - 1); + } + + if (io->usage_mask != 0xf) + t = type[io->num_components - 1]; + + if (io->last == io->first) + emit_hdrf(ctx, "%s %s %s %s;\n", prefix, inout, t, io->glsl_name); + else + emit_hdrf(ctx, "%s %s %s %s[%d];\n", prefix, inout, t, + io->glsl_name, size); +} + +static bool +can_emit_generic_default(UNUSED const struct vrend_shader_io *io) +{ + return true; +} + +static void emit_ios_vs(struct dump_ctx *ctx) +{ + uint32_t i; + + for (i = 0; i < ctx->num_inputs; i++) { + char postfix[32] = ""; + if (!ctx->inputs[i].glsl_predefined_no_emit) { + if (ctx->cfg->use_explicit_locations) { + emit_hdrf(ctx, "layout(location=%d) ", ctx->inputs[i].first); + } + if (ctx->inputs[i].first != ctx->inputs[i].last) + snprintf(postfix, sizeof(postfix), "[%d]", ctx->inputs[i].last - ctx->inputs[i].first + 1); + emit_hdrf(ctx, "in vec4 %s%s;\n", ctx->inputs[i].glsl_name, postfix); + } + } + + emit_ios_indirect_generics_output(ctx, ""); + + emit_ios_generic_outputs(ctx, can_emit_generic_default); + + if (ctx->key->color_two_side || ctx->force_color_two_side) { + bool fcolor_emitted, bcolor_emitted; + + for (i = 0; i < ctx->num_outputs; i++) { + if (ctx->outputs[i].sid >= 2) + continue; + + fcolor_emitted = bcolor_emitted = false; + + fcolor_emitted = ctx->front_back_color_emitted_flags[ctx->outputs[i].sid] & FRONT_COLOR_EMITTED; + bcolor_emitted = ctx->front_back_color_emitted_flags[ctx->outputs[i].sid] & BACK_COLOR_EMITTED; + + if (fcolor_emitted && !bcolor_emitted) { + emit_hdrf(ctx, "%sout vec4 ex_bc%d;\n", INTERP_PREFIX, ctx->outputs[i].sid); + ctx->front_back_color_emitted_flags[ctx->outputs[i].sid] |= BACK_COLOR_EMITTED; + } + if (bcolor_emitted && !fcolor_emitted) { + emit_hdrf(ctx, "%sout vec4 ex_c%d;\n", INTERP_PREFIX, ctx->outputs[i].sid); + ctx->front_back_color_emitted_flags[ctx->outputs[i].sid] |= FRONT_COLOR_EMITTED; + } + } + } + + emit_winsys_correction(ctx); + + if (ctx->has_clipvertex) { + emit_hdrf(ctx, "%svec4 clipv_tmp;\n", ctx->has_clipvertex_so ? "out " : ""); + } + if (ctx->num_clip_dist || ctx->key->clip_plane_enable) { + bool has_prop = (ctx->num_clip_dist_prop + ctx->num_cull_dist_prop) > 0; + int num_clip_dists = ctx->num_clip_dist ? ctx->num_clip_dist : 8; + int num_cull_dists = 0; + char cull_buf[64] = ""; + char clip_buf[64] = ""; + if (has_prop) { + num_clip_dists = ctx->num_clip_dist_prop; + num_cull_dists = ctx->num_cull_dist_prop; + if (num_clip_dists) + snprintf(clip_buf, 64, "out float gl_ClipDistance[%d];\n", num_clip_dists); + if (num_cull_dists) + snprintf(cull_buf, 64, "out float gl_CullDistance[%d];\n", num_cull_dists); + } else + snprintf(clip_buf, 64, "out float gl_ClipDistance[%d];\n", num_clip_dists); + if (ctx->key->clip_plane_enable) { + emit_hdr(ctx, "uniform vec4 clipp[8];\n"); + } + if (ctx->key->gs_present || ctx->key->tes_present) { + ctx->vs_has_pervertex = true; + emit_hdrf(ctx, "out gl_PerVertex {\n vec4 gl_Position;\n float gl_PointSize;\n%s%s};\n", clip_buf, cull_buf); + } + emit_hdr(ctx, "vec4 clip_dist_temp[2];\n"); + } +} + +static const char *get_depth_layout(int depth_layout) +{ + const char *dl[4] = { + "depth_any", + "depth_greater", + "depth_less", + "depth_unchanged" + }; + + if (depth_layout < 1 || depth_layout > TGSI_FS_DEPTH_LAYOUT_UNCHANGED) + return NULL; + return dl[depth_layout -1]; +} + +static void emit_ios_fs(struct dump_ctx *ctx) +{ + uint32_t i; + + if (ctx->early_depth_stencil) { + emit_hdr(ctx, "layout(early_fragment_tests) in;\n"); + } + + emit_ios_indirect_generics_input(ctx, ""); + + for (i = 0; i < ctx->num_inputs; i++) { + if (!ctx->inputs[i].glsl_predefined_no_emit) { + const char *prefix = ""; + const char *auxprefix = ""; + + if (ctx->inputs[i].name == TGSI_SEMANTIC_GENERIC || + ctx->inputs[i].name == TGSI_SEMANTIC_COLOR || + ctx->inputs[i].name == TGSI_SEMANTIC_BCOLOR) { + prefix = get_interp_string(ctx->cfg, ctx->inputs[i].interpolate, ctx->key->flatshade); + if (!prefix) + prefix = ""; + auxprefix = get_aux_string(ctx->inputs[i].location); + ctx->num_interps++; + } + + char prefixes[64]; + snprintf(prefixes, sizeof(prefixes), "%s %s", prefix, auxprefix); + emit_ios_generic(ctx, io_in, prefixes, &ctx->inputs[i], "in", ""); + } + + if (!ctx->winsys_adjust_y_emitted && + (ctx->key->coord_replace & (1 << ctx->inputs[i].sid))) { + ctx->winsys_adjust_y_emitted = true; + emit_hdr(ctx, "uniform float winsys_adjust_y;\n"); + } + } + + if (ctx->key->color_two_side) { + if (ctx->color_in_mask & 1) + emit_hdr(ctx, "vec4 realcolor0;\n"); + if (ctx->color_in_mask & 2) + emit_hdr(ctx, "vec4 realcolor1;\n"); + } + + if (ctx->write_all_cbufs) { + for (i = 0; i < (uint32_t)ctx->cfg->max_draw_buffers; i++) { + if (ctx->key->fs_logicop_enabled) + emit_hdrf(ctx, "vec4 fsout_tmp_c%d;\n", i); + + if (logiop_require_inout(ctx->key)) { + const char *noncoherent = ctx->key->fs_logicop_emulate_coherent ? "" : ", noncoherent"; + emit_hdrf(ctx, "layout (location=%d%s) inout highp vec4 fsout_c%d;\n", i, noncoherent, i); + } else + emit_hdrf(ctx, "layout (location=%d) out vec4 fsout_c%d;\n", i, i); + } + } else { + for (i = 0; i < ctx->num_outputs; i++) { + + if (!ctx->outputs[i].glsl_predefined_no_emit) { + emit_ios_generic(ctx, io_out, "", &ctx->outputs[i], + ctx->outputs[i].fbfetch_used ? "inout" : "out", ""); + + } else if (ctx->outputs[i].invariant || ctx->outputs[i].precise) { + emit_hdrf(ctx, "%s%s;\n", + ctx->outputs[i].precise ? "precise " : + (ctx->outputs[i].invariant ? "invariant " : ""), + ctx->outputs[i].glsl_name); + } + } + } + + if (ctx->fs_depth_layout) { + const char *depth_layout = get_depth_layout(ctx->fs_depth_layout); + if (depth_layout) + emit_hdrf(ctx, "layout (%s) out float gl_FragDepth;\n", depth_layout); + } + + if (ctx->num_in_clip_dist) { + if (ctx->key->prev_stage_num_clip_out) { + emit_hdrf(ctx, "in float gl_ClipDistance[%d];\n", ctx->key->prev_stage_num_clip_out); + } else if (ctx->num_in_clip_dist > 4 && !ctx->key->prev_stage_num_cull_out) { + emit_hdrf(ctx, "in float gl_ClipDistance[%d];\n", ctx->num_in_clip_dist); + } + + if (ctx->key->prev_stage_num_cull_out) { + emit_hdrf(ctx, "in float gl_CullDistance[%d];\n", ctx->key->prev_stage_num_cull_out); + } + if(ctx->fs_uses_clipdist_input) + emit_hdr(ctx, "vec4 clip_dist_temp[2];\n"); + } +} + +static bool +can_emit_generic_geom(const struct vrend_shader_io *io) +{ + return io->stream == 0; +} + +static void emit_ios_geom(struct dump_ctx *ctx) +{ + uint32_t i; + char invocbuf[25]; + + if (ctx->gs_num_invocations) + snprintf(invocbuf, 25, ", invocations = %d", ctx->gs_num_invocations); + + emit_hdrf(ctx, "layout(%s%s) in;\n", prim_to_name(ctx->gs_in_prim), + ctx->gs_num_invocations > 1 ? invocbuf : ""); + emit_hdrf(ctx, "layout(%s, max_vertices = %d) out;\n", prim_to_name(ctx->gs_out_prim), ctx->gs_max_out_verts); + + + for (i = 0; i < ctx->num_inputs; i++) { + if (!ctx->inputs[i].glsl_predefined_no_emit) { + char postfix[64]; + snprintf(postfix, sizeof(postfix), "[%d]", gs_input_prim_to_size(ctx->gs_in_prim)); + emit_ios_generic(ctx, io_in, "", &ctx->inputs[i], "in", postfix); + } + } + + for (i = 0; i < ctx->num_outputs; i++) { + if (!ctx->outputs[i].glsl_predefined_no_emit) { + if (!ctx->outputs[i].stream) + continue; + + const char *prefix = ""; + if (ctx->outputs[i].name == TGSI_SEMANTIC_GENERIC || + ctx->outputs[i].name == TGSI_SEMANTIC_COLOR || + ctx->outputs[i].name == TGSI_SEMANTIC_BCOLOR) { + ctx->num_interps++; + /* ugly leave spaces to patch interp in later */ + prefix = INTERP_PREFIX; + } + + emit_hdrf(ctx, "layout (stream = %d) %s%s%sout vec4 %s;\n", ctx->outputs[i].stream, prefix, + ctx->outputs[i].precise ? "precise " : "", + ctx->outputs[i].invariant ? "invariant " : "", + ctx->outputs[i].glsl_name); + } + } + + emit_ios_generic_outputs(ctx, can_emit_generic_geom); + + emit_winsys_correction(ctx); + + if (ctx->num_in_clip_dist || ctx->key->clip_plane_enable || ctx->key->prev_stage_pervertex_out) { + int clip_dist, cull_dist; + char clip_var[64] = ""; + char cull_var[64] = ""; + + clip_dist = ctx->key->prev_stage_num_clip_out ? ctx->key->prev_stage_num_clip_out : ctx->num_in_clip_dist; + cull_dist = ctx->key->prev_stage_num_cull_out; + + if (clip_dist) + snprintf(clip_var, 64, "float gl_ClipDistance[%d];\n", clip_dist); + if (cull_dist) + snprintf(cull_var, 64, "float gl_CullDistance[%d];\n", cull_dist); + + emit_hdrf(ctx, "in gl_PerVertex {\n vec4 gl_Position;\n float gl_PointSize; \n %s%s\n} gl_in[];\n", clip_var, cull_var); + } + if (ctx->num_clip_dist) { + bool has_prop = (ctx->num_clip_dist_prop + ctx->num_cull_dist_prop) > 0; + int num_clip_dists = ctx->num_clip_dist ? ctx->num_clip_dist : 8; + int num_cull_dists = 0; + char cull_buf[64] = ""; + char clip_buf[64] = ""; + if (has_prop) { + num_clip_dists = ctx->num_clip_dist_prop; + num_cull_dists = ctx->num_cull_dist_prop; + if (num_clip_dists) + snprintf(clip_buf, 64, "out float gl_ClipDistance[%d];\n", num_clip_dists); + if (num_cull_dists) + snprintf(cull_buf, 64, "out float gl_CullDistance[%d];\n", num_cull_dists); + } else + snprintf(clip_buf, 64, "out float gl_ClipDistance[%d];\n", num_clip_dists); + emit_hdrf(ctx, "%s%s\n", clip_buf, cull_buf); + emit_hdrf(ctx, "vec4 clip_dist_temp[2];\n"); + } +} + + +static void emit_ios_tcs(struct dump_ctx *ctx) +{ + uint32_t i; + + emit_ios_indirect_generics_input(ctx, "[]"); + + for (i = 0; i < ctx->num_inputs; i++) { + if (!ctx->inputs[i].glsl_predefined_no_emit) { + if (ctx->inputs[i].name == TGSI_SEMANTIC_PATCH) + emit_ios_patch(ctx, "", &ctx->inputs[i], "in", ctx->inputs[i].last - ctx->inputs[i].first + 1); + else + emit_ios_generic(ctx, io_in, "", &ctx->inputs[i], "in", "[]"); + } + } + + emit_hdrf(ctx, "layout(vertices = %d) out;\n", ctx->tcs_vertices_out); + + emit_ios_indirect_generics_output(ctx, "[]"); + + if (ctx->patch_output_range.used) + emit_ios_patch(ctx, "patch", &ctx->patch_output_range.io, "out", + ctx->patch_output_range.io.last - ctx->patch_output_range.io.sid + 1); + + for (i = 0; i < ctx->num_outputs; i++) { + if (!ctx->outputs[i].glsl_predefined_no_emit) { + if (ctx->outputs[i].name == TGSI_SEMANTIC_PATCH) { + emit_ios_patch(ctx, "patch", &ctx->outputs[i], "out", + ctx->outputs[i].last - ctx->outputs[i].first + 1); + } else + emit_ios_generic(ctx, io_out, "", &ctx->outputs[i], "out", "[]"); + } else if (ctx->outputs[i].invariant || ctx->outputs[i].precise) { + emit_hdrf(ctx, "%s%s;\n", + ctx->outputs[i].precise ? "precise " : + (ctx->outputs[i].invariant ? "invariant " : ""), + ctx->outputs[i].glsl_name); + } + } + + if (ctx->num_in_clip_dist || ctx->key->prev_stage_pervertex_out) { + int clip_dist, cull_dist; + char clip_var[64] = "", cull_var[64] = ""; + + clip_dist = ctx->key->prev_stage_num_clip_out ? ctx->key->prev_stage_num_clip_out : ctx->num_in_clip_dist; + cull_dist = ctx->key->prev_stage_num_cull_out; + + if (clip_dist) + snprintf(clip_var, 64, "float gl_ClipDistance[%d];\n", clip_dist); + if (cull_dist) + snprintf(cull_var, 64, "float gl_CullDistance[%d];\n", cull_dist); + + emit_hdrf(ctx, "in gl_PerVertex {\n vec4 gl_Position;\n float gl_PointSize; \n %s%s} gl_in[];\n", clip_var, cull_var); + } + if (ctx->num_clip_dist) { + emit_hdrf(ctx, "out gl_PerVertex {\n vec4 gl_Position;\n float gl_PointSize;\n float gl_ClipDistance[%d];\n} gl_out[];\n", ctx->num_clip_dist ? ctx->num_clip_dist : 8); + emit_hdr(ctx, "vec4 clip_dist_temp[2];\n"); + } +} + +static void emit_ios_tes(struct dump_ctx *ctx) +{ + uint32_t i; + + if (ctx->patch_input_range.used) + emit_ios_patch(ctx, "patch", &ctx->patch_input_range.io, "in", + ctx->patch_input_range.io.last - ctx->patch_input_range.io.sid + 1); + + if (ctx->generic_input_range.used) + emit_ios_indirect_generics_input(ctx, "[]"); + + for (i = 0; i < ctx->num_inputs; i++) { + if (!ctx->inputs[i].glsl_predefined_no_emit) { + if (ctx->inputs[i].name == TGSI_SEMANTIC_PATCH) + emit_ios_patch(ctx, "patch", &ctx->inputs[i], "in", + ctx->inputs[i].last - ctx->inputs[i].first + 1); + else + emit_ios_generic(ctx, io_in, "", &ctx->inputs[i], "in", "[]"); + } + } + + emit_hdrf(ctx, "layout(%s, %s, %s%s) in;\n", + prim_to_tes_name(ctx->tes_prim_mode), + get_spacing_string(ctx->tes_spacing), + ctx->tes_vertex_order ? "cw" : "ccw", + ctx->tes_point_mode ? ", point_mode" : ""); + + emit_ios_generic_outputs(ctx, can_emit_generic_default); + + emit_winsys_correction(ctx); + + if (ctx->num_in_clip_dist || ctx->key->prev_stage_pervertex_out) { + int clip_dist, cull_dist; + char clip_var[64] = "", cull_var[64] = ""; + + clip_dist = ctx->key->prev_stage_num_clip_out ? ctx->key->prev_stage_num_clip_out : ctx->num_in_clip_dist; + cull_dist = ctx->key->prev_stage_num_cull_out; + + if (clip_dist) + snprintf(clip_var, 64, "float gl_ClipDistance[%d];\n", clip_dist); + if (cull_dist) + snprintf(cull_var, 64, "float gl_CullDistance[%d];\n", cull_dist); + + emit_hdrf(ctx, "in gl_PerVertex {\n vec4 gl_Position;\n float gl_PointSize; \n %s%s} gl_in[];\n", clip_var, cull_var); + } + if (ctx->num_clip_dist) { + emit_hdrf(ctx, "out gl_PerVertex {\n vec4 gl_Position;\n float gl_PointSize;\n float gl_ClipDistance[%d];\n} gl_out[];\n", ctx->num_clip_dist ? ctx->num_clip_dist : 8); + emit_hdr(ctx, "vec4 clip_dist_temp[2];\n"); + } +} + + +static void emit_ios_cs(struct dump_ctx *ctx) +{ + emit_hdrf(ctx, "layout (local_size_x = %d, local_size_y = %d, local_size_z = %d) in;\n", + ctx->local_cs_block_size[0], ctx->local_cs_block_size[1], ctx->local_cs_block_size[2]); + + if (ctx->req_local_mem) { + enum vrend_type_qualifier type = ctx->integer_memory ? INT : UINT; + emit_hdrf(ctx, "shared %s values[%d];\n", get_string(type), ctx->req_local_mem / 4); + } +} + +static void emit_ios(struct dump_ctx *ctx) +{ + ctx->num_interps = 0; + + if (ctx->so && ctx->so->num_outputs >= PIPE_MAX_SO_OUTPUTS) { + set_hdr_error(ctx); + return; + } + + switch (ctx->prog_type) { + case TGSI_PROCESSOR_VERTEX: + emit_ios_vs(ctx); + break; + case TGSI_PROCESSOR_FRAGMENT: + emit_ios_fs(ctx); + break; + case TGSI_PROCESSOR_GEOMETRY: + emit_ios_geom(ctx); + break; + case TGSI_PROCESSOR_TESS_CTRL: + emit_ios_tcs(ctx); + break; + case TGSI_PROCESSOR_TESS_EVAL: + emit_ios_tes(ctx); + break; + case TGSI_PROCESSOR_COMPUTE: + emit_ios_cs(ctx); + break; + default: + set_hdr_error(ctx); + return; + } + + if (ctx->generic_outputs_expected_mask && + (ctx->generic_outputs_expected_mask != ctx->generic_outputs_emitted_mask)) { + for (int i = 0; i < 31; ++i) { + uint32_t mask = 1 << i; + bool expecting = ctx->generic_outputs_expected_mask & mask; + if (expecting & !(ctx->generic_outputs_emitted_mask & mask)) + emit_hdrf(ctx, " out vec4 %s_g%dA0_f%s;\n", + get_stage_output_name_prefix(ctx->prog_type), i, + ctx->prog_type == TGSI_PROCESSOR_TESS_CTRL ? "[]" : ""); + } + } + + emit_ios_streamout(ctx); + emit_ios_common(ctx); + + if (ctx->prog_type == TGSI_PROCESSOR_FRAGMENT && + ctx->key->pstipple_tex == true) { + emit_hdr(ctx, "uniform sampler2D pstipple_sampler;\nfloat stip_temp;\n"); + } +} + +static boolean fill_fragment_interpolants(struct dump_ctx *ctx, struct vrend_shader_info *sinfo) +{ + uint32_t i, index = 0; + + for (i = 0; i < ctx->num_inputs; i++) { + if (ctx->inputs[i].glsl_predefined_no_emit) + continue; + + if (ctx->inputs[i].name != TGSI_SEMANTIC_GENERIC && + ctx->inputs[i].name != TGSI_SEMANTIC_COLOR) + continue; + + if (index >= ctx->num_interps) + return true; + + sinfo->interpinfo[index].semantic_name = ctx->inputs[i].name; + sinfo->interpinfo[index].semantic_index = ctx->inputs[i].sid; + sinfo->interpinfo[index].interpolate = ctx->inputs[i].interpolate; + sinfo->interpinfo[index].location = ctx->inputs[i].location; + index++; + } + return true; +} + +static boolean fill_interpolants(struct dump_ctx *ctx, struct vrend_shader_info *sinfo) +{ + boolean ret; + + if (!ctx->num_interps) + return true; + if (ctx->prog_type == TGSI_PROCESSOR_VERTEX || ctx->prog_type == TGSI_PROCESSOR_GEOMETRY) + return true; + + free(sinfo->interpinfo); + sinfo->interpinfo = calloc(ctx->num_interps, sizeof(struct vrend_interp_info)); + if (!sinfo->interpinfo) + return false; + + ret = fill_fragment_interpolants(ctx, sinfo); + if (ret == false) + goto out_fail; + + return true; + out_fail: + free(sinfo->interpinfo); + return false; +} + +static boolean analyze_instruction(struct tgsi_iterate_context *iter, + struct tgsi_full_instruction *inst) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + uint32_t opcode = inst->Instruction.Opcode; + if (opcode == TGSI_OPCODE_ATOMIMIN || opcode == TGSI_OPCODE_ATOMIMAX) { + const struct tgsi_full_src_register *src = &inst->Src[0]; + if (src->Register.File == TGSI_FILE_BUFFER) + ctx->ssbo_integer_mask |= 1 << src->Register.Index; + if (src->Register.File == TGSI_FILE_MEMORY) + ctx->integer_memory = true; + } + + if (!ctx->fs_uses_clipdist_input && (ctx->prog_type == TGSI_PROCESSOR_FRAGMENT)) { + for (int i = 0; i < inst->Instruction.NumSrcRegs; ++i) { + if (inst->Src[i].Register.File == TGSI_FILE_INPUT) { + int idx = inst->Src[i].Register.Index; + for (unsigned j = 0; j < ctx->num_inputs; ++j) { + if (ctx->inputs[j].first <= idx && ctx->inputs[j].last >= idx && + ctx->inputs[j].name == TGSI_SEMANTIC_CLIPDIST) { + ctx->fs_uses_clipdist_input = true; + break; + } + } + } + } + } + + + return true; +} + +static void fill_sinfo(struct dump_ctx *ctx, struct vrend_shader_info *sinfo) +{ + sinfo->num_ucp = ctx->key->clip_plane_enable ? 8 : 0; + sinfo->has_pervertex_out = ctx->vs_has_pervertex; + sinfo->has_sample_input = ctx->has_sample_input; + bool has_prop = (ctx->num_clip_dist_prop + ctx->num_cull_dist_prop) > 0; + sinfo->num_clip_out = has_prop ? ctx->num_clip_dist_prop : (ctx->num_clip_dist ? ctx->num_clip_dist : 8); + sinfo->num_cull_out = has_prop ? ctx->num_cull_dist_prop : 0; + sinfo->samplers_used_mask = ctx->samplers_used; + sinfo->images_used_mask = ctx->images_used_mask; + sinfo->num_consts = ctx->num_consts; + sinfo->ubo_used_mask = ctx->ubo_used_mask; + + sinfo->ssbo_used_mask = ctx->ssbo_used_mask; + + sinfo->ubo_indirect = ctx->info.dimension_indirect_files & (1 << TGSI_FILE_CONSTANT); + + if (ctx->generic_input_range.used) + sinfo->num_indirect_generic_inputs = ctx->generic_input_range.io.last - ctx->generic_input_range.io.sid + 1; + if (ctx->patch_input_range.used) + sinfo->num_indirect_patch_inputs = ctx->patch_input_range.io.last - ctx->patch_input_range.io.sid + 1; + + if (ctx->generic_output_range.used) + sinfo->num_indirect_generic_outputs = ctx->generic_output_range.io.last - ctx->generic_output_range.io.sid + 1; + if (ctx->patch_output_range.used) + sinfo->num_indirect_patch_outputs = ctx->patch_output_range.io.last - ctx->patch_output_range.io.sid + 1; + + sinfo->num_inputs = ctx->num_inputs; + sinfo->num_interps = ctx->num_interps; + sinfo->num_outputs = ctx->num_outputs; + sinfo->shadow_samp_mask = ctx->shadow_samp_mask; + sinfo->glsl_ver = ctx->glsl_ver_required; + sinfo->gs_out_prim = ctx->gs_out_prim; + sinfo->tes_prim = ctx->tes_prim_mode; + sinfo->tes_point_mode = ctx->tes_point_mode; + + if (sinfo->so_names || ctx->so_names) { + if (sinfo->so_names) { + for (unsigned i = 0; i < sinfo->so_info.num_outputs; ++i) + free(sinfo->so_names[i]); + free(sinfo->so_names); + } + } + + /* Record information about the layout of generics and patches for apssing it + * to the next shader stage. mesa/tgsi doesn't provide this information for + * TCS, TES, and GEOM shaders. + */ + sinfo->guest_sent_io_arrays = ctx->guest_sent_io_arrays; + sinfo->num_generic_and_patch_outputs = 0; + for(unsigned i = 0; i < ctx->num_outputs; i++) { + sinfo->generic_outputs_layout[sinfo->num_generic_and_patch_outputs].name = ctx->outputs[i].name; + sinfo->generic_outputs_layout[sinfo->num_generic_and_patch_outputs].sid = ctx->outputs[i].sid; + sinfo->generic_outputs_layout[sinfo->num_generic_and_patch_outputs].location = ctx->outputs[i].layout_location; + sinfo->generic_outputs_layout[sinfo->num_generic_and_patch_outputs].array_id = ctx->outputs[i].array_id; + sinfo->generic_outputs_layout[sinfo->num_generic_and_patch_outputs].usage_mask = ctx->outputs[i].usage_mask; + if (ctx->outputs[i].name == TGSI_SEMANTIC_GENERIC || ctx->outputs[i].name == TGSI_SEMANTIC_PATCH) { + sinfo->num_generic_and_patch_outputs++; + } + } + + sinfo->so_names = ctx->so_names; + sinfo->attrib_input_mask = ctx->attrib_input_mask; + if (sinfo->sampler_arrays) + free(sinfo->sampler_arrays); + sinfo->sampler_arrays = ctx->sampler_arrays; + sinfo->num_sampler_arrays = ctx->num_sampler_arrays; + if (sinfo->image_arrays) + free(sinfo->image_arrays); + sinfo->image_arrays = ctx->image_arrays; + sinfo->num_image_arrays = ctx->num_image_arrays; + sinfo->generic_inputs_emitted_mask = ctx->generic_inputs_emitted_mask; + + for (unsigned i = 0; i < ctx->num_outputs; ++i) { + if (ctx->outputs[i].invariant) + sinfo->invariant_outputs |= 1ull << ctx->outputs[i].sid; + } +} + +static bool allocate_strbuffers(struct dump_ctx* ctx) +{ + if (!strbuf_alloc(&ctx->glsl_main, 4096)) + return false; + + if (strbuf_get_error(&ctx->glsl_main)) + return false; + + if (!strbuf_alloc(&ctx->glsl_hdr, 1024)) + return false; + + if (!strbuf_alloc(&ctx->glsl_ver_ext, 1024)) + return false; + + return true; +} + +static void set_strbuffers(MAYBE_UNUSED struct vrend_context *rctx, struct dump_ctx* ctx, + struct vrend_strarray *shader) +{ + strarray_addstrbuf(shader, &ctx->glsl_ver_ext); + strarray_addstrbuf(shader, &ctx->glsl_hdr); + strarray_addstrbuf(shader, &ctx->glsl_main); +} + +bool vrend_convert_shader(struct vrend_context *rctx, + struct vrend_shader_cfg *cfg, + const struct tgsi_token *tokens, + uint32_t req_local_mem, + struct vrend_shader_key *key, + struct vrend_shader_info *sinfo, + struct vrend_strarray *shader) +{ + struct dump_ctx ctx; + boolean bret; + + memset(&ctx, 0, sizeof(struct dump_ctx)); + + /* First pass to deal with edge cases. */ + if (ctx.prog_type == TGSI_PROCESSOR_FRAGMENT) + ctx.iter.iterate_declaration = iter_inputs; + ctx.iter.iterate_instruction = analyze_instruction; + bret = tgsi_iterate_shader(tokens, &ctx.iter); + if (bret == false) + return false; + + ctx.num_inputs = 0; + + ctx.iter.prolog = prolog; + ctx.iter.iterate_instruction = iter_instruction; + ctx.iter.iterate_declaration = iter_declaration; + ctx.iter.iterate_immediate = iter_immediate; + ctx.iter.iterate_property = iter_property; + ctx.iter.epilog = NULL; + ctx.key = key; + ctx.cfg = cfg; + ctx.prog_type = -1; + ctx.num_image_arrays = 0; + ctx.image_arrays = NULL; + ctx.num_sampler_arrays = 0; + ctx.sampler_arrays = NULL; + ctx.ssbo_array_base = 0xffffffff; + ctx.ssbo_atomic_array_base = 0xffffffff; + ctx.has_sample_input = false; + ctx.req_local_mem = req_local_mem; + ctx.guest_sent_io_arrays = key->guest_sent_io_arrays; + ctx.generic_outputs_expected_mask = key->generic_outputs_expected_mask; + + tgsi_scan_shader(tokens, &ctx.info); + + if (cfg->glsl_version >= 140) + require_glsl_ver(&ctx, 140); + + if (sinfo->so_info.num_outputs) { + ctx.so = &sinfo->so_info; + ctx.so_names = calloc(sinfo->so_info.num_outputs, sizeof(char *)); + if (!ctx.so_names) + goto fail; + } else + ctx.so_names = NULL; + + if (ctx.info.dimension_indirect_files & (1 << TGSI_FILE_CONSTANT)) + require_glsl_ver(&ctx, 150); + + if (ctx.info.indirect_files & (1 << TGSI_FILE_BUFFER) || + ctx.info.indirect_files & (1 << TGSI_FILE_IMAGE)) { + require_glsl_ver(&ctx, 150); + ctx.shader_req_bits |= SHADER_REQ_GPU_SHADER5; + } + if (ctx.info.indirect_files & (1 << TGSI_FILE_SAMPLER)) + ctx.shader_req_bits |= SHADER_REQ_GPU_SHADER5; + + if (!allocate_strbuffers(&ctx)) + goto fail; + + bret = tgsi_iterate_shader(tokens, &ctx.iter); + if (bret == false) + goto fail; + + for (size_t i = 0; i < ARRAY_SIZE(ctx.src_bufs); ++i) + strbuf_free(ctx.src_bufs + i); + + emit_header(&ctx); + emit_ios(&ctx); + + if (strbuf_get_error(&ctx.glsl_hdr)) + goto fail; + + bret = fill_interpolants(&ctx, sinfo); + if (bret == false) + goto fail; + + free(ctx.temp_ranges); + + fill_sinfo(&ctx, sinfo); + set_strbuffers(rctx, &ctx, shader); + + return true; + fail: + strbuf_free(&ctx.glsl_main); + strbuf_free(&ctx.glsl_hdr); + strbuf_free(&ctx.glsl_ver_ext); + free(ctx.so_names); + free(ctx.temp_ranges); + return false; +} + +static void replace_interp(struct vrend_strarray *program, + const char *var_name, + const char *pstring, const char *auxstring) +{ + int mylen = strlen(INTERP_PREFIX) + strlen("out float "); + + char *ptr = program->strings[SHADER_STRING_HDR].buf; + do { + char *p = strstr(ptr, var_name); + if (!p) + break; + + ptr = p - mylen; + + memset(ptr, ' ', strlen(INTERP_PREFIX)); + memcpy(ptr, pstring, strlen(pstring)); + memcpy(ptr + strlen(pstring), auxstring, strlen(auxstring)); + + ptr = p + strlen(var_name); + } while (1); +} + +static const char *gpu_shader5_and_msinterp_string = + "#extension GL_OES_gpu_shader5 : require\n" + "#extension GL_OES_shader_multisample_interpolation : require\n"; + +static void require_gpu_shader5_and_msinterp(struct vrend_strarray *program) +{ + strbuf_append(&program->strings[SHADER_STRING_VER_EXT], gpu_shader5_and_msinterp_string); +} + +bool vrend_patch_vertex_shader_interpolants(MAYBE_UNUSED struct vrend_context *rctx, + struct vrend_shader_cfg *cfg, + struct vrend_strarray *prog_strings, + struct vrend_shader_info *vs_info, + struct vrend_shader_info *fs_info, + const char *oprefix, bool flatshade) +{ + int i; + const char *pstring, *auxstring; + char glsl_name[64]; + if (!vs_info || !fs_info) + return true; + + if (!fs_info->interpinfo) + return true; + + if (fs_info->has_sample_input) { + if (cfg->glsl_version < 320) + require_gpu_shader5_and_msinterp(prog_strings); + } + + for (i = 0; i < fs_info->num_interps; i++) { + pstring = get_interp_string(cfg, fs_info->interpinfo[i].interpolate, flatshade); + if (!pstring) + continue; + + auxstring = get_aux_string(fs_info->interpinfo[i].location); + + switch (fs_info->interpinfo[i].semantic_name) { + case TGSI_SEMANTIC_COLOR: + case TGSI_SEMANTIC_BCOLOR: + /* color is a bit trickier */ + if (fs_info->glsl_ver < 140) { + if (fs_info->interpinfo[i].semantic_index == 1) { + replace_interp(prog_strings, "gl_FrontSecondaryColor", pstring, auxstring); + replace_interp(prog_strings, "gl_BackSecondaryColor", pstring, auxstring); + } else { + replace_interp(prog_strings, "gl_FrontColor", pstring, auxstring); + replace_interp(prog_strings, "gl_BackColor", pstring, auxstring); + } + } else { + snprintf(glsl_name, 64, "ex_c%d", fs_info->interpinfo[i].semantic_index); + replace_interp(prog_strings, glsl_name, pstring, auxstring); + snprintf(glsl_name, 64, "ex_bc%d", fs_info->interpinfo[i].semantic_index); + replace_interp(prog_strings, glsl_name, pstring, auxstring); + } + break; + case TGSI_SEMANTIC_GENERIC: + snprintf(glsl_name, 64, "%s_g%d", oprefix, fs_info->interpinfo[i].semantic_index); + replace_interp(prog_strings, glsl_name, pstring, auxstring); + break; + default: + return false; + } + } + + return true; +} + +static boolean +iter_vs_declaration(struct tgsi_iterate_context *iter, + struct tgsi_full_declaration *decl) +{ + struct dump_ctx *ctx = (struct dump_ctx *)iter; + + const char *shader_in_prefix = "vso"; + const char *shader_out_prefix = "tco"; + const char *name_prefix = ""; + unsigned i; + unsigned mask_temp; + + // Generate a shader that passes through all VS outputs + if (decl->Declaration.File == TGSI_FILE_OUTPUT) { + for (uint32_t j = 0; j < ctx->num_inputs; j++) { + if (ctx->inputs[j].name == decl->Semantic.Name && + ctx->inputs[j].sid == decl->Semantic.Index && + ctx->inputs[j].first == decl->Range.First && + ctx->inputs[j].usage_mask == decl->Declaration.UsageMask && + ((!decl->Declaration.Array && ctx->inputs[j].array_id == 0) || + (ctx->inputs[j].array_id == decl->Array.ArrayID))) + return true; + } + i = ctx->num_inputs++; + + ctx->inputs[i].name = decl->Semantic.Name; + ctx->inputs[i].sid = decl->Semantic.Index; + ctx->inputs[i].interpolate = decl->Interp.Interpolate; + ctx->inputs[i].location = decl->Interp.Location; + ctx->inputs[i].first = decl->Range.First; + ctx->inputs[i].layout_location = 0; + ctx->inputs[i].last = decl->Range.Last; + ctx->inputs[i].array_id = decl->Declaration.Array ? decl->Array.ArrayID : 0; + ctx->inputs[i].usage_mask = mask_temp = decl->Declaration.UsageMask; + u_bit_scan_consecutive_range(&mask_temp, &ctx->inputs[i].swizzle_offset, &ctx->inputs[i].num_components); + + ctx->inputs[i].glsl_predefined_no_emit = false; + ctx->inputs[i].glsl_no_index = false; + ctx->inputs[i].override_no_wm = ctx->inputs[i].num_components == 1; + ctx->inputs[i].glsl_gl_block = false; + + switch (ctx->inputs[i].name) { + case TGSI_SEMANTIC_PSIZE: + name_prefix = "gl_PointSize"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].override_no_wm = true; + ctx->inputs[i].glsl_gl_block = true; + ctx->shader_req_bits |= SHADER_REQ_PSIZE; + break; + + case TGSI_SEMANTIC_CLIPDIST: + name_prefix = "gl_ClipDistance"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].glsl_gl_block = true; + ctx->num_in_clip_dist += 4 * (ctx->inputs[i].last - ctx->inputs[i].first + 1); + ctx->shader_req_bits |= SHADER_REQ_CLIP_DISTANCE; + if (ctx->inputs[i].last != ctx->inputs[i].first) + ctx->guest_sent_io_arrays = true; + break; + + case TGSI_SEMANTIC_POSITION: + name_prefix = "gl_Position"; + ctx->inputs[i].glsl_predefined_no_emit = true; + ctx->inputs[i].glsl_no_index = true; + ctx->inputs[i].glsl_gl_block = true; + break; + + case TGSI_SEMANTIC_PATCH: + case TGSI_SEMANTIC_GENERIC: + if (ctx->inputs[i].first != ctx->inputs[i].last || + ctx->inputs[i].array_id > 0) { + ctx->guest_sent_io_arrays = true; + } + break; + } + + memcpy(&ctx->outputs[i], &ctx->inputs[i], sizeof(struct vrend_shader_io)); + + if (ctx->inputs[i].glsl_no_index) { + snprintf(ctx->inputs[i].glsl_name, 128, "%s", name_prefix); + snprintf(ctx->outputs[i].glsl_name, 128, "%s", name_prefix); + } else { + if (ctx->inputs[i].name == TGSI_SEMANTIC_FOG){ + ctx->inputs[i].usage_mask = 0xf; + ctx->inputs[i].num_components = 4; + ctx->inputs[i].swizzle_offset = 0; + ctx->inputs[i].override_no_wm = false; + snprintf(ctx->inputs[i].glsl_name, 64, "%s_f%d", shader_in_prefix, ctx->inputs[i].sid); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_f%d", shader_out_prefix, ctx->inputs[i].sid); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_COLOR) { + snprintf(ctx->inputs[i].glsl_name, 64, "%s_c%d", shader_in_prefix, ctx->inputs[i].sid); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_c%d", shader_out_prefix, ctx->inputs[i].sid); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_GENERIC) { + snprintf(ctx->inputs[i].glsl_name, 64, "%s_g%dA%d_%x", + shader_in_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_g%dA%d_%x", + shader_out_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + } else if (ctx->inputs[i].name == TGSI_SEMANTIC_PATCH) { + snprintf(ctx->inputs[i].glsl_name, 64, "%s_p%dA%d_%x", + shader_in_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + snprintf(ctx->outputs[i].glsl_name, 64, "%s_p%dA%d_%x", + shader_out_prefix, ctx->inputs[i].sid, + ctx->inputs[i].array_id, ctx->inputs[i].usage_mask); + } else { + snprintf(ctx->outputs[i].glsl_name, 64, "%s_%d", shader_in_prefix, ctx->inputs[i].first); + snprintf(ctx->inputs[i].glsl_name, 64, "%s_%d", shader_out_prefix, ctx->inputs[i].first); + } + } + } + return true; +} + +bool vrend_shader_create_passthrough_tcs(struct vrend_context *rctx, + struct vrend_shader_cfg *cfg, + struct tgsi_token *vs_tokens, + struct vrend_shader_key *key, + const float tess_factors[6], + struct vrend_shader_info *sinfo, + struct vrend_strarray *shader, + int vertices_per_patch) +{ + struct dump_ctx ctx; + + memset(&ctx, 0, sizeof(struct dump_ctx)); + + ctx.prog_type = TGSI_PROCESSOR_TESS_CTRL; + ctx.cfg = cfg; + ctx.key = key; + ctx.iter.iterate_declaration = iter_vs_declaration; + ctx.ssbo_array_base = 0xffffffff; + ctx.ssbo_atomic_array_base = 0xffffffff; + ctx.has_sample_input = false; + + if (!allocate_strbuffers(&ctx)) + goto fail; + + tgsi_iterate_shader(vs_tokens, &ctx.iter); + + /* What is the default on GL? */ + ctx.tcs_vertices_out = vertices_per_patch; + + ctx.num_outputs = ctx.num_inputs; + + handle_io_arrays(&ctx); + + emit_header(&ctx); + emit_ios(&ctx); + + emit_buf(&ctx, "void main() {\n"); + + for (unsigned int i = 0; i < ctx.num_inputs; ++i) { + const char *out_prefix = ""; + const char *in_prefix = ""; + + const char *postfix = ""; + + if (ctx.inputs[i].glsl_gl_block) { + out_prefix = "gl_out[gl_InvocationID]."; + in_prefix = "gl_in[gl_InvocationID]."; + } else { + postfix = "[gl_InvocationID]"; + } + + if (ctx.inputs[i].first == ctx.inputs[i].last) { + emit_buff(&ctx, "%s%s%s = %s%s%s;\n", + out_prefix, ctx.outputs[i].glsl_name, postfix, + in_prefix, ctx.inputs[i].glsl_name, postfix); + } else { + unsigned size = ctx.inputs[i].last == ctx.inputs[i].first + 1; + for (unsigned int k = 0; k < size; ++k) { + emit_buff(&ctx, "%s%s%s[%d] = %s%s%s[%d];\n", + out_prefix, ctx.outputs[i].glsl_name, postfix, k, + in_prefix, ctx.inputs[i].glsl_name, postfix, k); + } + } + } + + for (int i = 0; i < 4; ++i) + emit_buff(&ctx, "gl_TessLevelOuter[%d] = %f;\n", i, tess_factors[i]); + + for (int i = 0; i < 2; ++i) + emit_buff(&ctx, "gl_TessLevelInner[%d] = %f;\n", i, tess_factors[i + 4]); + + emit_buf(&ctx, "}\n"); + + fill_sinfo(&ctx, sinfo); + set_strbuffers(rctx, &ctx, shader); + return true; +fail: + strbuf_free(&ctx.glsl_main); + strbuf_free(&ctx.glsl_hdr); + strbuf_free(&ctx.glsl_ver_ext); + free(ctx.so_names); + free(ctx.temp_ranges); + return false; +} diff --git a/app/src/main/cpp/virglrenderer/src/vrend_shader.h b/app/src/main/cpp/virglrenderer/src/vrend_shader.h new file mode 100644 index 0000000..c39cfa3 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_shader.h @@ -0,0 +1,171 @@ +/************************************************************************** + * + * Copyright (C) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef VREND_SHADER_H +#define VREND_SHADER_H + +#include "pipe/p_state.h" +#include "pipe/p_shader_tokens.h" + +#include "vrend_strbuf.h" +/* need to store patching info for interpolation */ +struct vrend_interp_info { + int semantic_name; + int semantic_index; + int interpolate; + unsigned location; +}; + +struct vrend_array { + int first; + int array_size; +}; + +struct vrend_layout_info { + unsigned name; + int sid; + int location; + int array_id; + int usage_mask; +}; + +struct vrend_shader_info { + uint32_t samplers_used_mask; + uint32_t images_used_mask; + uint32_t ubo_used_mask; + uint32_t ssbo_used_mask; + uint32_t num_generic_and_patch_outputs; + bool guest_sent_io_arrays; + struct vrend_layout_info generic_outputs_layout[64]; + int num_consts; + int num_inputs; + int num_interps; + int num_outputs; + bool ubo_indirect; + uint8_t num_indirect_generic_outputs; + uint8_t num_indirect_patch_outputs; + uint8_t num_indirect_generic_inputs; + uint8_t num_indirect_patch_inputs; + uint32_t generic_inputs_emitted_mask; + int num_ucp; + int glsl_ver; + bool has_pervertex_out; + bool has_sample_input; + uint8_t num_clip_out; + uint8_t num_cull_out; + uint32_t shadow_samp_mask; + int gs_out_prim; + int tes_prim; + bool tes_point_mode; + uint32_t attrib_input_mask; + + struct vrend_array *sampler_arrays; + int num_sampler_arrays; + + struct vrend_array *image_arrays; + int num_image_arrays; + + struct pipe_stream_output_info so_info; + + struct vrend_interp_info *interpinfo; + char **so_names; + uint64_t invariant_outputs; +}; + +struct vrend_shader_key { + uint32_t coord_replace; + bool pstipple_tex; + bool add_alpha_test; + bool color_two_side; + uint8_t alpha_test; + uint8_t clip_plane_enable; + bool gs_present; + bool tcs_present; + bool tes_present; + bool flatshade; + bool prev_stage_pervertex_out; + bool guest_sent_io_arrays; + bool fs_logicop_enabled; + bool fs_logicop_emulate_coherent; + enum pipe_logicop fs_logicop_func; + uint8_t surface_component_bits[PIPE_MAX_COLOR_BUFS]; + + uint32_t num_prev_generic_and_patch_outputs; + struct vrend_layout_info prev_stage_generic_and_patch_outputs_layout[64]; + + uint8_t prev_stage_num_clip_out; + uint8_t prev_stage_num_cull_out; + float alpha_ref_val; + uint32_t cbufs_are_a8_bitmask; + uint8_t num_indirect_generic_outputs; + uint8_t num_indirect_patch_outputs; + uint8_t num_indirect_generic_inputs; + uint8_t num_indirect_patch_inputs; + uint32_t generic_outputs_expected_mask; + uint8_t fs_swizzle_output_rgb_to_bgr; +}; + +struct vrend_shader_cfg { + int glsl_version; + int max_draw_buffers; + bool use_explicit_locations; + bool has_es31_compat; +}; + +struct vrend_context; + +#define SHADER_MAX_STRINGS 3 +#define SHADER_STRING_VER_EXT 0 +#define SHADER_STRING_HDR 1 + +bool vrend_patch_vertex_shader_interpolants(struct vrend_context *rctx, + struct vrend_shader_cfg *cfg, + struct vrend_strarray *shader, + struct vrend_shader_info *vs_info, + struct vrend_shader_info *fs_info, + const char *oprefix, bool flatshade); + +bool vrend_convert_shader(struct vrend_context *rctx, + struct vrend_shader_cfg *cfg, + const struct tgsi_token *tokens, + uint32_t req_local_mem, + struct vrend_shader_key *key, + struct vrend_shader_info *sinfo, + struct vrend_strarray *shader); + +const char *vrend_shader_samplertypeconv(int sampler_type); + +char vrend_shader_samplerreturnconv(enum tgsi_return_type type); + +int vrend_shader_lookup_sampler_array(struct vrend_shader_info *sinfo, int index); + +bool vrend_shader_create_passthrough_tcs(struct vrend_context *ctx, + struct vrend_shader_cfg *cfg, + struct tgsi_token *vs_info, + struct vrend_shader_key *key, + const float tess_factors[6], + struct vrend_shader_info *sinfo, + struct vrend_strarray *shader, + int vertices_per_patch); +#endif diff --git a/app/src/main/cpp/virglrenderer/src/vrend_strbuf.h b/app/src/main/cpp/virglrenderer/src/vrend_strbuf.h new file mode 100644 index 0000000..3f7cfc0 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_strbuf.h @@ -0,0 +1,195 @@ +/************************************************************************** + * + * Copyright (C) 2019 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#ifndef VREND_STRBUF_H +#define VREND_STRBUF_H + +#include +#include +#include +#include +#include +#include "util/u_math.h" + +/* shader string buffer */ +struct vrend_strbuf { + /* NULL terminated string storage */ + char *buf; + /* allocation size (must be >= strlen(str) + 1) */ + size_t alloc_size; + /* size of string stored without terminating NULL */ + size_t size; + bool error_state; +}; + +static inline void strbuf_set_error(struct vrend_strbuf *sb) +{ + sb->error_state = true; +} + +static inline bool strbuf_get_error(struct vrend_strbuf *sb) +{ + return sb->error_state; +} + +static inline size_t strbuf_get_len(struct vrend_strbuf *sb) +{ + return sb->size; +} + +static inline void strbuf_free(struct vrend_strbuf *sb) +{ + free(sb->buf); +} + +static inline bool strbuf_alloc(struct vrend_strbuf *sb, int initial_size) +{ + sb->buf = malloc(initial_size); + if (!sb->buf) + return false; + sb->alloc_size = initial_size; + sb->buf[0] = 0; + sb->error_state = false; + sb->size = 0; + return true; +} + +/* this might need tuning */ +#define STRBUF_MIN_MALLOC 1024 + +static inline bool strbuf_grow(struct vrend_strbuf *sb, int len) +{ + if (sb->size + len + 1 > sb->alloc_size) { + /* Reallocate to the larger size of current alloc + min realloc, + * or the resulting string size if larger. + */ + size_t new_size = MAX2(sb->size + len + 1, sb->alloc_size + STRBUF_MIN_MALLOC); + char *new = realloc(sb->buf, new_size); + if (!new) { + strbuf_set_error(sb); + return false; + } + sb->buf = new; + sb->alloc_size = new_size; + } + return true; +} + +static inline void strbuf_append_buffer(struct vrend_strbuf *sb, const char *data, size_t len) +{ + assert(!memchr(data, '\0', len)); + if (strbuf_get_error(sb) || + !strbuf_grow(sb, len)) + return; + memcpy(sb->buf + sb->size, data, len); + sb->size += len; + sb->buf[sb->size] = '\0'; +} + +static inline void strbuf_append(struct vrend_strbuf *sb, const char *addstr) +{ + strbuf_append_buffer(sb, addstr, strlen(addstr)); +} + +static inline void strbuf_vappendf(struct vrend_strbuf *sb, const char *fmt, va_list ap) +{ + va_list cp; + va_copy(cp, ap); + + int len = vsnprintf(sb->buf + sb->size, sb->alloc_size - sb->size, fmt, ap); + if (len >= (int)(sb->alloc_size - sb->size)) { + if (!strbuf_grow(sb, len)) + return; + vsnprintf(sb->buf + sb->size, sb->alloc_size - sb->size, fmt, cp); + } + sb->size += len; +} + +__attribute__((format(printf, 2, 3))) +static inline void strbuf_appendf(struct vrend_strbuf *sb, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + strbuf_vappendf(sb, fmt, va); + va_end(va); +} + +static inline void strbuf_vfmt(struct vrend_strbuf *sb, const char *fmt, va_list ap) +{ + va_list cp; + va_copy(cp, ap); + + int len = vsnprintf(sb->buf, sb->alloc_size, fmt, ap); + if (len >= (int)(sb->alloc_size)) { + if (!strbuf_grow(sb, len)) + return; + vsnprintf(sb->buf, sb->alloc_size, fmt, cp); + } + sb->size = len; +} + +__attribute__((format(printf, 2, 3))) +static inline void strbuf_fmt(struct vrend_strbuf *sb, const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + strbuf_vfmt(sb, fmt, va); + va_end(va); +} + +struct vrend_strarray { + int num_strings; + int num_alloced_strings; + struct vrend_strbuf *strings; +}; + +static inline bool strarray_alloc(struct vrend_strarray *sa, int init_alloc) +{ + sa->num_strings = 0; + sa->num_alloced_strings = init_alloc; + sa->strings = calloc(init_alloc, sizeof(struct vrend_strbuf)); + if (!sa->strings) + return false; + return true; +} + +static inline bool strarray_addstrbuf(struct vrend_strarray *sa, struct vrend_strbuf *sb) +{ + assert(sa->num_strings < sa->num_alloced_strings); + if (sa->num_strings >= sa->num_alloced_strings) + return false; + sa->strings[sa->num_strings] = *sb; + sa->num_strings++; + return true; +} + +static inline void strarray_free(struct vrend_strarray *sa, bool free_strings) +{ + if (free_strings) { + for (int i = 0; i < sa->num_strings; i++) + strbuf_free(&sa->strings[i]); + } + free(sa->strings); +} + +#endif diff --git a/app/src/main/cpp/virglrenderer/src/vrend_util.h b/app/src/main/cpp/virglrenderer/src/vrend_util.h new file mode 100644 index 0000000..ce22d89 --- /dev/null +++ b/app/src/main/cpp/virglrenderer/src/vrend_util.h @@ -0,0 +1,111 @@ +/************************************************************************** + * + * Copyright (C) 2019 Chromium. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#ifndef VREND_UTIL_H +#define VREND_UTIL_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define BIT(n) (UINT32_C(1) << (n)) +#define printf(...) __android_log_print(ANDROID_LOG_DEBUG, "System.out", __VA_ARGS__); + +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_QUADS 0x0007 +#define GL_QUAD_STRIP 0x0008 +#define GL_POLYGON 0x0009 + +static inline bool has_bit(uint32_t mask, uint32_t bit) +{ + return (mask & bit) != 0; +} + +static inline bool is_only_bit(uint32_t mask, uint32_t bit) +{ + return (mask == bit); +} + +static int vrend_gl_version() +{ + const char *version = (const char *)glGetString(GL_VERSION); + int major, minor; + + if (!version) + return 0; + + while (!isdigit(*version) && *version != '\0') + version++; + + sscanf(version, "%i.%i", &major, &minor); + return 10 * major + minor; +} + +static bool vrend_has_gl_extension(const char *ext) +{ + int num_extensions; + int i; + + glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); + if (num_extensions == 0) + return false; + + for (i = 0; i < num_extensions; i++) { + const char *gl_ext = (const char *)glGetStringi(GL_EXTENSIONS, i); + if (!gl_ext) + return false; + + if (strcmp(ext, gl_ext) == 0) + return true; + } + + return false; +} + +static void vrend_get_glsl_version(int *glsl_version) +{ + int major_local, minor_local; + const GLubyte *version_str; + int version; + + version_str = glGetString(GL_SHADING_LANGUAGE_VERSION); + char tmp[20]; + sscanf((const char *)version_str, "%s %s %s %s %i.%i", + tmp, tmp, tmp, tmp, &major_local, &minor_local); + + version = (major_local * 100) + minor_local; + if (glsl_version) + *glsl_version = version; +} + +#endif \ No newline at end of file diff --git a/app/src/main/java/com/winlator/ContainerDetailFragment.java b/app/src/main/java/com/winlator/ContainerDetailFragment.java index cdc0990..c2ddf14 100644 --- a/app/src/main/java/com/winlator/ContainerDetailFragment.java +++ b/app/src/main/java/com/winlator/ContainerDetailFragment.java @@ -31,6 +31,7 @@ import com.winlator.box86_64.Box86_64PresetManager; import com.winlator.container.Container; import com.winlator.container.ContainerManager; import com.winlator.contentdialog.AddEnvVarDialog; +import com.winlator.contentdialog.DXVKConfigDialog; import com.winlator.core.AppUtils; import com.winlator.core.Callback; import com.winlator.core.EnvVars; @@ -43,6 +44,7 @@ import com.winlator.core.WineThemeManager; import com.winlator.core.WineUtils; import com.winlator.widget.CPUListView; import com.winlator.widget.ColorPickerView; +import com.winlator.widget.ImagePickerView; import org.json.JSONArray; import org.json.JSONException; @@ -129,7 +131,13 @@ public class ContainerDetailFragment extends Fragment { final Spinner sGraphicsDriver = view.findViewById(R.id.SGraphicsDriver); final Spinner sDXWrapper = view.findViewById(R.id.SDXWrapper); - loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper); + + final View vDXWrapperConfig = view.findViewById(R.id.BTDXWrapperConfig); + vDXWrapperConfig.setTag(editContainer ? container.getDXWrapperConfig() : ""); + + setupDXWrapperSpinner(sDXWrapper, vDXWrapperConfig); + loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper, + editContainer ? container.getGraphicsDriver() : Container.DEFAULT_GRAPHICS_DRIVER, editContainer ? container.getDXWrapper() : Container.DEFAULT_DXWRAPPER); view.findViewById(R.id.BTHelpDXWrapper).setOnClickListener((v) -> AppUtils.showHelpBox(context, v, R.string.dxwrapper_help_content)); @@ -140,7 +148,7 @@ public class ContainerDetailFragment extends Fragment { cbShowFPS.setChecked(editContainer && container.isShowFPS()); final CheckBox cbStopServicesOnStartup = view.findViewById(R.id.CBStopServicesOnStartup); - cbStopServicesOnStartup.setChecked(editContainer && container.isStopServicesOnStartup()); + cbStopServicesOnStartup.setChecked(!editContainer || container.isStopServicesOnStartup()); final Spinner sBox86Preset = view.findViewById(R.id.SBox86Preset); Box86_64PresetManager.loadSpinner("box86", sBox86Preset, editContainer ? container.getBox86Preset() : preferences.getString("box86_preset", Box86_64Preset.COMPATIBILITY)); @@ -152,12 +160,12 @@ public class ContainerDetailFragment extends Fragment { if (editContainer) cpuListView.setCheckedCPUList(container.getCPUList()); createWineConfigurationTab(view); - createDXComponentsTab(view); + createWinComponentsTab(view); createEnvVarsTab(view); createDrivesTab(view); view.findViewById(R.id.BTAddEnvVar).setOnClickListener((v) -> (new AddEnvVarDialog(context, view)).show()); - AppUtils.setupTabLayout(view, R.id.TabLayout, R.id.LLTabWineConfiguration, R.id.LLTabDXComponents, R.id.LLTabEnvVars, R.id.LLTabDrives, R.id.LLTabAdvanced); + AppUtils.setupTabLayout(view, R.id.TabLayout, R.id.LLTabWineConfiguration, R.id.LLTabWinComponents, R.id.LLTabEnvVars, R.id.LLTabDrives, R.id.LLTabAdvanced); view.findViewById(R.id.BTConfirm).setOnClickListener((v) -> { try { @@ -166,8 +174,9 @@ public class ContainerDetailFragment extends Fragment { String envVars = getEnvVars(view); String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem()); String dxwrapper = StringUtils.parseIdentifier(sDXWrapper.getSelectedItem()); + String dxwrapperConfig = vDXWrapperConfig.getTag().toString(); String audioDriver = StringUtils.parseIdentifier(sAudioDriver.getSelectedItem()); - String dxcomponents = getDXComponents(view); + String wincomponents = getWinComponents(view); String drives = getDrives(view); boolean showFPS = cbShowFPS.isChecked(); String cpuList = cpuListView.getCheckedCPUListAsString(); @@ -183,8 +192,9 @@ public class ContainerDetailFragment extends Fragment { container.setCPUList(cpuList); container.setGraphicsDriver(graphicsDriver); container.setDXWrapper(dxwrapper); + container.setDXWrapperConfig(dxwrapperConfig); container.setAudioDriver(audioDriver); - container.setDXComponents(dxcomponents); + container.setWinComponents(wincomponents); container.setDrives(drives); container.setShowFPS(showFPS); container.setStopServicesOnStartup(stopServicesOnStartup); @@ -203,8 +213,9 @@ public class ContainerDetailFragment extends Fragment { data.put("cpuList", cpuList); data.put("graphicsDriver", graphicsDriver); data.put("dxwrapper", dxwrapper); + data.put("dxwrapperConfig", dxwrapperConfig); data.put("audioDriver", audioDriver); - data.put("dxcomponents", dxcomponents); + data.put("wincomponents", wincomponents); data.put("drives", drives); data.put("showFPS", showFPS); data.put("stopServicesOnStartup", stopServicesOnStartup); @@ -274,7 +285,7 @@ public class ContainerDetailFragment extends Fragment { registryEditor.setDwordValue("Software\\Wine\\Direct3D", "strict_shader_math", sStrictShaderMath.getSelectedItemPosition()); Spinner sVideoMemorySize = view.findViewById(R.id.SVideoMemorySize); - registryEditor.setStringValue("Software\\Wine\\Direct3D", "VideoMemorySize", sVideoMemorySize.getSelectedItem().toString()); + registryEditor.setStringValue("Software\\Wine\\Direct3D", "VideoMemorySize", StringUtils.parseNumber(sVideoMemorySize.getSelectedItem())); Spinner sMouseWarpOverride = view.findViewById(R.id.SMouseWarpOverride); registryEditor.setStringValue("Software\\Wine\\DirectInput", "MouseWarpOverride", sMouseWarpOverride.getSelectedItem().toString().toLowerCase(Locale.ENGLISH)); @@ -287,11 +298,33 @@ public class ContainerDetailFragment extends Fragment { private void createWineConfigurationTab(View view) { Context context = getContext(); - String[] desktopTheme = (container != null ? container.getDesktopTheme() : WineThemeManager.DEFAULT_THEME+","+WineThemeManager.DEFAULT_BACKGROUND).split(","); + WineThemeManager.ThemeInfo desktopTheme = new WineThemeManager.ThemeInfo(container != null ? container.getDesktopTheme() : WineThemeManager.DEFAULT_DESKTOP_THEME); Spinner sDesktopTheme = view.findViewById(R.id.SDesktopTheme); - sDesktopTheme.setSelection(WineThemeManager.Theme.valueOf(desktopTheme[0]).ordinal()); - ColorPickerView cpvDesktopBackground = view.findViewById(R.id.CPVDesktopBackground); - cpvDesktopBackground.setColor(Color.parseColor(desktopTheme[1])); + sDesktopTheme.setSelection(desktopTheme.theme.ordinal()); + final ImagePickerView ipvDesktopBackgroundImage = view.findViewById(R.id.IPVDesktopBackgroundImage); + final ColorPickerView cpvDesktopBackgroundColor = view.findViewById(R.id.CPVDesktopBackgroundColor); + cpvDesktopBackgroundColor.setColor(desktopTheme.backgroundColor); + + Spinner sDesktopBackgroundType = view.findViewById(R.id.SDesktopBackgroundType); + sDesktopBackgroundType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + WineThemeManager.BackgroundType type = WineThemeManager.BackgroundType.values()[position]; + ipvDesktopBackgroundImage.setVisibility(View.GONE); + cpvDesktopBackgroundColor.setVisibility(View.GONE); + + if (type == WineThemeManager.BackgroundType.IMAGE) { + ipvDesktopBackgroundImage.setVisibility(View.VISIBLE); + } + else if (type == WineThemeManager.BackgroundType.COLOR) { + cpvDesktopBackgroundColor.setVisibility(View.VISIBLE); + } + } + + @Override + public void onNothingSelected(AdapterView parent) {} + }); + sDesktopBackgroundType.setSelection(desktopTheme.backgroundType.ordinal()); File containerDir = container != null ? container.getRootDir() : null; File userRegFile = new File(containerDir, ".wine/user.reg"); @@ -316,7 +349,7 @@ public class ContainerDetailFragment extends Fragment { Spinner sVideoMemorySize = view.findViewById(R.id.SVideoMemorySize); String videoMemorySize = registryEditor.getStringValue("Software\\Wine\\Direct3D", "VideoMemorySize", "2048"); - loadVideoMemorySizeSpinner(sVideoMemorySize, videoMemorySize); + AppUtils.setSpinnerSelectionFromNumber(sVideoMemorySize, videoMemorySize); List mouseWarpOverrideList = Arrays.asList(context.getString(R.string.disable), context.getString(R.string.enable), context.getString(R.string.force)); Spinner sMouseWarpOverride = view.findViewById(R.id.SMouseWarpOverride); @@ -342,20 +375,6 @@ public class ContainerDetailFragment extends Fragment { spinner.setSelection(selectedPosition); } - private void loadVideoMemorySizeSpinner(Spinner spinner, String selectedValue) { - List values = new ArrayList<>(); - int selectedPosition = 0; - - for (int i = 5, j = 0; i <= 12; i++, j++) { - String value = String.valueOf((int)Math.pow(2, i)); - if (value.equals(selectedValue)) selectedPosition = j; - values.add(value); - } - - spinner.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_dropdown_item, values)); - spinner.setSelection(selectedPosition); - } - private String getEnvVars(View view) { LinearLayout parent = view.findViewById(R.id.LLEnvVars); EnvVars envVars = new EnvVars(); @@ -385,13 +404,21 @@ public class ContainerDetailFragment extends Fragment { } private String getDesktopTheme(View view) { + Spinner sDesktopBackgroundType = view.findViewById(R.id.SDesktopBackgroundType); + WineThemeManager.BackgroundType type = WineThemeManager.BackgroundType.values()[sDesktopBackgroundType.getSelectedItemPosition()]; Spinner sDesktopTheme = view.findViewById(R.id.SDesktopTheme); - ColorPickerView cpvDesktopBackground = view.findViewById(R.id.CPVDesktopBackground); + ColorPickerView cpvDesktopBackground = view.findViewById(R.id.CPVDesktopBackgroundColor); WineThemeManager.Theme theme = WineThemeManager.Theme.values()[sDesktopTheme.getSelectedItemPosition()]; - return theme+","+cpvDesktopBackground.getColorAsString(); + + String desktopTheme = theme+","+type+","+cpvDesktopBackground.getColorAsString(); + if (type == WineThemeManager.BackgroundType.IMAGE) { + File userWallpaperFile = WineThemeManager.getUserWallpaperFile(getContext()); + desktopTheme += ","+(userWallpaperFile.isFile() ? userWallpaperFile.lastModified() : "0"); + } + return desktopTheme; } - private void loadScreenSizeSpinner(View view, String selectedValue) { + public static void loadScreenSizeSpinner(View view, String selectedValue) { final Spinner sScreenSize = view.findViewById(R.id.SScreenSize); final LinearLayout llCustomScreenSize = view.findViewById(R.id.LLCustomScreenSize); @@ -415,19 +442,18 @@ public class ContainerDetailFragment extends Fragment { } } - private void loadGraphicsDriverSpinner(Spinner sGraphicsDriver, Spinner sDXWrapper) { - final Context context = getContext(); + public static void loadGraphicsDriverSpinner(final Spinner sGraphicsDriver, final Spinner sDXWrapper, String selectedGraphicsDriver, String selectedDXWrapper) { + final Context context = sGraphicsDriver.getContext(); final String[] dxwrapperEntries = context.getResources().getStringArray(R.array.dxwrapper_entries); Runnable update = () -> { String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem()); - boolean useDXVK = graphicsDriver.equals("turnip-zink"); + boolean useDXVK = graphicsDriver.equals("turnip"); ArrayList items = new ArrayList<>(); for (String value : dxwrapperEntries) if (useDXVK || (!value.startsWith("DXVK") && !value.startsWith("D8VK"))) items.add(value); sDXWrapper.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, items.toArray(new String[0]))); - - AppUtils.setSpinnerSelectionFromIdentifier(sDXWrapper, container != null ? container.getDXWrapper() : Container.DEFAULT_DXWRAPPER); + AppUtils.setSpinnerSelectionFromIdentifier(sDXWrapper, selectedDXWrapper); }; sGraphicsDriver.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @@ -440,35 +466,51 @@ public class ContainerDetailFragment extends Fragment { public void onNothingSelected(AdapterView parent) {} }); - String selectedGraphicsDriver = container != null ? container.getGraphicsDriver() : Container.DEFAULT_GRAPHICS_DRIVER; AppUtils.setSpinnerSelectionFromIdentifier(sGraphicsDriver, selectedGraphicsDriver); update.run(); } - private String getDXComponents(View view) { - ViewGroup parent = view.findViewById(R.id.LLTabDXComponents); + public static void setupDXWrapperSpinner(Spinner sDXWrapper, View vDXWrapperConfig) { + sDXWrapper.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + String dxwrapper = StringUtils.parseIdentifier(sDXWrapper.getSelectedItem()); + if (dxwrapper.equals("dxvk")) { + vDXWrapperConfig.setOnClickListener((v) -> (new DXVKConfigDialog(vDXWrapperConfig)).show()); + vDXWrapperConfig.setVisibility(View.VISIBLE); + } + else vDXWrapperConfig.setVisibility(View.GONE); + } + + @Override + public void onNothingSelected(AdapterView parent) {} + }); + } + + private String getWinComponents(View view) { + ViewGroup parent = view.findViewById(R.id.LLTabWinComponents); int childCount = parent.getChildCount(); - String[] dxcomponents = new String[childCount]; + String[] wincomponents = new String[childCount]; for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); Spinner spinner = child.findViewById(R.id.Spinner); - dxcomponents[i] = child.getTag().toString()+"="+spinner.getSelectedItemPosition(); + wincomponents[i] = child.getTag().toString()+"="+spinner.getSelectedItemPosition(); } - return String.join(",", dxcomponents); + return String.join(",", wincomponents); } - private void createDXComponentsTab(View view) { - final String[] dxcomponents = (container != null ? container.getDXComponents() : Container.DEFAULT_DXCOMPONENTS).split(","); + private void createWinComponentsTab(View view) { + final String[] wincomponents = (container != null ? container.getWinComponents() : Container.DEFAULT_WINCOMPONENTS).split(","); Context context = getContext(); LayoutInflater inflater = LayoutInflater.from(context); - ViewGroup parent = view.findViewById(R.id.LLTabDXComponents); + ViewGroup parent = view.findViewById(R.id.LLTabWinComponents); - for (String dxcomponent : dxcomponents) { - String[] parts = dxcomponent.split("="); - View itemView = inflater.inflate(R.layout.dxcomponent_list_item, parent, false); + for (String wincomponent : wincomponents) { + String[] parts = wincomponent.split("="); + View itemView = inflater.inflate(R.layout.wincomponent_list_item, parent, false); ((TextView)itemView.findViewById(R.id.TextView)).setText(StringUtils.getString(context, parts[0])); ((Spinner)itemView.findViewById(R.id.Spinner)).setSelection(Integer.parseInt(parts[1]), false); itemView.setTag(parts[0]); @@ -491,7 +533,7 @@ public class ContainerDetailFragment extends Fragment { } private void createDrivesTab(View view) { - Context context = getContext(); + final Context context = getContext(); final LinearLayout parent = view.findViewById(R.id.LLDrives); final View emptyTextView = view.findViewById(R.id.TVDrivesEmptyText); diff --git a/app/src/main/java/com/winlator/ExternalControllerBindingsActivity.java b/app/src/main/java/com/winlator/ExternalControllerBindingsActivity.java index 2df609f..95acc3b 100644 --- a/app/src/main/java/com/winlator/ExternalControllerBindingsActivity.java +++ b/app/src/main/java/com/winlator/ExternalControllerBindingsActivity.java @@ -218,7 +218,17 @@ public class ExternalControllerBindingsActivity extends AppCompatActivity { @Override public void onNothingSelected(AdapterView parent) {} }); - holder.bindingType.setSelection(item.getBinding().isKeyboard() ? 0 : item.getBinding().isMouse() : 1 ? 2, false); + + Binding selectedBinding = item.getBinding(); + if (selectedBinding.isKeyboard()) { + holder.bindingType.setSelection(0, false); + } + else if (selectedBinding.isMouse()) { + holder.bindingType.setSelection(1, false); + } + else if (selectedBinding.isGamepad()) { + holder.bindingType.setSelection(2, false); + } holder.binding.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override diff --git a/app/src/main/java/com/winlator/MainActivity.java b/app/src/main/java/com/winlator/MainActivity.java index 25645b1..3849884 100644 --- a/app/src/main/java/com/winlator/MainActivity.java +++ b/app/src/main/java/com/winlator/MainActivity.java @@ -28,14 +28,13 @@ import androidx.fragment.app.FragmentManager; import com.google.android.material.navigation.NavigationView; import com.winlator.contentdialog.ContentDialog; import com.winlator.core.Callback; -import com.winlator.core.OBBImageInstaller; +import com.winlator.xenvironment.ImageFsInstaller; import com.winlator.core.PreloaderDialog; -import com.winlator.xenvironment.ImageFs; import java.util.List; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { - public static final byte DEBUG_LEVEL = 0; // FIXME set 0 to disable + public static final byte DEBUG_LEVEL = 0; // FIXME [0=disable, 2=wine, 3=box86_64, 4=box86_64+] public static final @IntRange(from = 1, to = 19) byte CONTAINER_PATTERN_COMPRESSION_LEVEL = 9; public static final byte PERMISSION_WRITE_EXTERNAL_STORAGE_REQUEST_CODE = 1; public static final byte OPEN_FILE_REQUEST_CODE = 2; @@ -75,7 +74,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On actionBar.setHomeAsUpIndicator(R.drawable.icon_action_bar_menu); onNavigationItemSelected(navigationView.getMenu().findItem(menuItemId)); navigationView.setCheckedItem(menuItemId); - if (!requestAppPermissions()) OBBImageInstaller.installIfNeeded(this); + if (!requestAppPermissions()) ImageFsInstaller.installIfNeeded(this); } } @@ -84,7 +83,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_WRITE_EXTERNAL_STORAGE_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - OBBImageInstaller.installIfNeeded(this); + ImageFsInstaller.installIfNeeded(this); } else finish(); } @@ -190,8 +189,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On tvDeveloper.setText(Html.fromHtml("by BrunoSX (winlator.org)", Html.FROM_HTML_MODE_LEGACY)); tvDeveloper.setMovementMethod(LinkMovementMethod.getInstance()); - ((TextView)dialog.findViewById(R.id.TVAppVersion)).setText(getString(R.string.app_version)+" "+pInfo.versionName); - ((TextView)dialog.findViewById(R.id.TVOBBImageVersion)).setText(getString(R.string.obb_image_version)+" "+ImageFs.find(this).getFormattedVersion()); + ((TextView)dialog.findViewById(R.id.TVAppVersion)).setText(getString(R.string.version)+" "+pInfo.versionName); String creditsAndThirdPartyAppsHTML = String.join("
", "Ubuntu RootFs (Focal Fossa)", diff --git a/app/src/main/java/com/winlator/SettingsFragment.java b/app/src/main/java/com/winlator/SettingsFragment.java index 6c48286..92c024f 100644 --- a/app/src/main/java/com/winlator/SettingsFragment.java +++ b/app/src/main/java/com/winlator/SettingsFragment.java @@ -5,7 +5,6 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -14,7 +13,6 @@ import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; -import android.widget.PopupMenu; import android.widget.SeekBar; import android.widget.Spinner; import android.widget.TextView; @@ -36,9 +34,8 @@ import com.winlator.box86_64.Box86_64EditPresetDialog; import com.winlator.core.AppUtils; import com.winlator.core.Callback; import com.winlator.core.FileUtils; -import com.winlator.core.GPUInformation; -import com.winlator.core.OBBImageInstaller; import com.winlator.core.PreloaderDialog; +import com.winlator.core.DefaultVersion; import com.winlator.core.StringUtils; import com.winlator.core.WineInfo; import com.winlator.core.WineUtils; @@ -52,16 +49,10 @@ import java.util.List; import java.util.concurrent.Executors; public class SettingsFragment extends Fragment { - public static final String DEFAULT_BOX86_VERSION = "0.3.0"; - public static final String DEFAULT_BOX64_VERSION = "0.2.6"; private Callback selectWineFileCallback; private PreloaderDialog preloaderDialog; private SharedPreferences preferences; - public static String getDefaultTurnipVersion(Context context) { - return GPUInformation.isAdreno6xx(context) ? "23.1.6" : "23.3.0"; - } - @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -96,28 +87,21 @@ public class SettingsFragment extends Fragment { preferences = PreferenceManager.getDefaultSharedPreferences(context); final Spinner sBox86Version = view.findViewById(R.id.SBox86Version); - String box86Version = preferences.getString("box86_version", DEFAULT_BOX86_VERSION); + String box86Version = preferences.getString("box86_version", DefaultVersion.BOX86); if (!AppUtils.setSpinnerSelectionFromIdentifier(sBox86Version, box86Version)) { - AppUtils.setSpinnerSelectionFromIdentifier(sBox86Version, DEFAULT_BOX86_VERSION); + AppUtils.setSpinnerSelectionFromIdentifier(sBox86Version, DefaultVersion.BOX86); } final Spinner sBox64Version = view.findViewById(R.id.SBox64Version); - String box64Version = preferences.getString("box64_version", DEFAULT_BOX64_VERSION); + String box64Version = preferences.getString("box64_version", DefaultVersion.BOX64); if (!AppUtils.setSpinnerSelectionFromIdentifier(sBox64Version, box64Version)) { - AppUtils.setSpinnerSelectionFromIdentifier(sBox64Version, DEFAULT_BOX64_VERSION); + AppUtils.setSpinnerSelectionFromIdentifier(sBox64Version, DefaultVersion.BOX64); } final Spinner sBox86Preset = view.findViewById(R.id.SBox86Preset); final Spinner sBox64Preset = view.findViewById(R.id.SBox64Preset); loadBox86_64PresetSpinners(view, sBox86Preset, sBox64Preset); - final Spinner sTurnipVersion = view.findViewById(R.id.STurnipVersion); - String defaultTurnipVersion = getDefaultTurnipVersion(context); - String turnipVersion = preferences.getString("turnip_version", defaultTurnipVersion); - if (!AppUtils.setSpinnerSelectionFromIdentifier(sTurnipVersion, turnipVersion)) { - AppUtils.setSpinnerSelectionFromIdentifier(sTurnipVersion, defaultTurnipVersion); - } - final CheckBox cbUseDRI3 = view.findViewById(R.id.CBUseDRI3); cbUseDRI3.setChecked(preferences.getBoolean("use_dri3", true)); @@ -166,40 +150,17 @@ public class SettingsFragment extends Fragment { getActivity().startActivityFromFragment(this, intent, MainActivity.OPEN_FILE_REQUEST_CODE); }); - final Runnable updateUI = () -> { - String obbImageVersion = ImageFs.find(context).getFormattedVersion(); - ((TextView)view.findViewById(R.id.TVOBBImageVersion)).setText(context.getString(R.string.installed_version)+" "+obbImageVersion); - }; - updateUI.run(); - - view.findViewById(R.id.BTInstallOBBImage).setOnClickListener((v) -> { - final MainActivity activity = (MainActivity)getActivity(); - PopupMenu popupMenu = new PopupMenu(context, v); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) popupMenu.setForceShowIcon(true); - popupMenu.inflate(R.menu.open_file_popup_menu); - popupMenu.setOnMenuItemClickListener((menuItem) -> { - int itemId = menuItem.getItemId(); - if (itemId == R.id.open_file) { - OBBImageInstaller.openFileForInstall(activity, updateUI); - } - else if (itemId == R.id.download_file) { - OBBImageInstaller.downloadFileForInstall(activity, updateUI); - } - return true; - }); - popupMenu.show(); - }); - view.findViewById(R.id.BTConfirm).setOnClickListener((v) -> { SharedPreferences.Editor editor = preferences.edit(); editor.putString("box86_version", StringUtils.parseIdentifier(sBox86Version.getSelectedItem())); editor.putString("box64_version", StringUtils.parseIdentifier(sBox64Version.getSelectedItem())); editor.putString("box86_preset", Box86_64PresetManager.getSpinnerSelectedId(sBox86Preset)); editor.putString("box64_preset", Box86_64PresetManager.getSpinnerSelectedId(sBox64Preset)); - editor.putString("turnip_version", StringUtils.parseIdentifier(sTurnipVersion.getSelectedItem())); editor.putBoolean("use_dri3", cbUseDRI3.isChecked()); editor.putFloat("cursor_speed", sbCursorSpeed.getProgress() / 100.0f); + if (preferences.contains("turnip_version")) editor.remove("turnip_version"); + if (editor.commit()) { NavigationView navigationView = getActivity().findViewById(R.id.NavigationView); navigationView.setCheckedItem(R.id.main_menu_containers); @@ -365,8 +326,8 @@ public class SettingsFragment extends Fragment { public static void resetBox86_64Version(AppCompatActivity activity) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); SharedPreferences.Editor editor = preferences.edit(); - editor.putString("box86_version", DEFAULT_BOX86_VERSION); - editor.putString("box64_version", DEFAULT_BOX64_VERSION); + editor.putString("box86_version", DefaultVersion.BOX86); + editor.putString("box64_version", DefaultVersion.BOX64); editor.remove("current_box86_version"); editor.remove("current_box64_version"); editor.apply(); diff --git a/app/src/main/java/com/winlator/XServerDisplayActivity.java b/app/src/main/java/com/winlator/XServerDisplayActivity.java index dabf61b..ef70ee3 100644 --- a/app/src/main/java/com/winlator/XServerDisplayActivity.java +++ b/app/src/main/java/com/winlator/XServerDisplayActivity.java @@ -24,26 +24,31 @@ import com.google.android.material.navigation.NavigationView; import com.winlator.container.Container; import com.winlator.container.ContainerManager; import com.winlator.container.Shortcut; +import com.winlator.contentdialog.ContentDialog; +import com.winlator.contentdialog.DXVKConfigDialog; import com.winlator.core.AppUtils; import com.winlator.core.CursorLocker; import com.winlator.core.EnvVars; import com.winlator.core.FileUtils; -import com.winlator.core.OBBImageInstaller; +import com.winlator.core.KeyValueSet; import com.winlator.core.OnExtractFileListener; import com.winlator.core.PreloaderDialog; import com.winlator.core.ProcessHelper; -import com.winlator.core.TarZstdUtils; +import com.winlator.core.TarCompressorUtils; +import com.winlator.core.DefaultVersion; import com.winlator.core.WineInfo; import com.winlator.core.WineRegistryEditor; import com.winlator.core.WineStartMenuCreator; import com.winlator.core.WineThemeManager; import com.winlator.core.WineUtils; +import com.winlator.core.Workarounds; import com.winlator.inputcontrols.ControlsProfile; import com.winlator.inputcontrols.ExternalController; import com.winlator.inputcontrols.InputControlsManager; +import com.winlator.math.Mathf; import com.winlator.renderer.GLRenderer; -import com.winlator.contentdialog.ContentDialog; import com.winlator.widget.InputControlsView; +import com.winlator.widget.MagnifierView; import com.winlator.widget.TouchpadView; import com.winlator.widget.XServerView; import com.winlator.winhandler.TaskManagerDialog; @@ -70,6 +75,8 @@ import org.json.JSONObject; import java.io.File; import java.util.ArrayList; import java.util.Iterator; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.Executors; public class XServerDisplayActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { @@ -87,6 +94,8 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat private Shortcut shortcut; private String graphicsDriver = Container.DEFAULT_GRAPHICS_DRIVER; private String audioDriver = Container.DEFAULT_AUDIO_DRIVER; + private String dxwrapper = Container.DEFAULT_DXWRAPPER; + private KeyValueSet dxwrapperConfig; private WineInfo wineInfo; private final EnvVars envVars = new EnvVars(); private boolean firstTimeBoot = false; @@ -94,6 +103,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat private OnExtractFileListener onExtractFileListener; private final WinHandler winHandler = new WinHandler(this); private float globalCursorSpeed = 1.0f; + private MagnifierView magnifierView; @Override public void onCreate(Bundle savedInstanceState) { @@ -133,21 +143,28 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat graphicsDriver = container.getGraphicsDriver(); audioDriver = container.getAudioDriver(); + dxwrapper = container.getDXWrapper(); + String dxwrapperConfig = container.getDXWrapperConfig(); screenSize = container.getScreenSize(); if (shortcut != null) { graphicsDriver = shortcut.getExtra("graphicsDriver", container.getGraphicsDriver()); audioDriver = shortcut.getExtra("audioDriver", container.getAudioDriver()); + dxwrapper = shortcut.getExtra("dxwrapper", container.getDXWrapper()); + dxwrapperConfig = shortcut.getExtra("dxwrapperConfig", container.getDXWrapperConfig()); screenSize = shortcut.getExtra("screenSize", container.getScreenSize()); String dinputMapperType = shortcut.getExtra("dinputMapperType"); if (!dinputMapperType.isEmpty()) winHandler.setDInputMapperType(Byte.parseByte(dinputMapperType)); } + if (dxwrapper.equals("dxvk")) this.dxwrapperConfig = DXVKConfigDialog.parseConfig(dxwrapperConfig); + if (!wineInfo.isWin64()) { - onExtractFileListener = (destination, entryName) -> { - if (entryName.contains("system32")) return null; - return new File(destination, entryName.replace("syswow64", "system32")); + onExtractFileListener = (file, size) -> { + String name = file.getName(); + if (name.contains("system32/")) return null; + return new File(file.getParentFile(), name.replace("syswow64/", "system32/")); }; } } @@ -155,15 +172,21 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat inputControlsManager = new InputControlsManager(this); xServer = new XServer(new ScreenInfo(screenSize)); xServer.setWinHandler(winHandler); + boolean[] started = {false}; xServer.windowManager.addOnWindowModificationListener(new WindowManager.OnWindowModificationListener() { @Override public void onUpdateWindowContent(Window window) { - if (window.getWidth() > 1) { + if (!started[0] && window.getWidth() > 1) { xServerView.getRenderer().setCursorVisible(true); preloaderDialog.closeOnUiThread(); - xServer.windowManager.removeOnWindowModificationListener(this); + started[0] = true; } } + + @Override + public void onMapWindow(Window window) { + Workarounds.onMapWindow(winHandler, window); + } }); setupUI(); @@ -222,6 +245,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { + final GLRenderer renderer = xServerView.getRenderer(); switch (item.getItemId()) { case R.id.main_menu_keyboard: AppUtils.showKeyboard(this); @@ -232,13 +256,30 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat drawerLayout.closeDrawers(); break; case R.id.main_menu_toggle_fullscreen: - xServerView.getRenderer().toggleFullscreen(); + renderer.toggleFullscreen(); drawerLayout.closeDrawers(); break; case R.id.main_menu_task_manager: (new TaskManagerDialog(this)).show(); drawerLayout.closeDrawers(); break; + case R.id.main_menu_magnifier: + if (magnifierView == null) { + final FrameLayout container = findViewById(R.id.FLXServerDisplay); + magnifierView = new MagnifierView(this); + magnifierView.setZoomButtonCallback((value) -> { + renderer.setMagnifierZoom(Mathf.clamp(renderer.getMagnifierZoom() + value, 1.0f, 3.0f)); + magnifierView.setZoomValue(renderer.getMagnifierZoom()); + }); + magnifierView.setZoomValue(renderer.getMagnifierZoom()); + magnifierView.setHideButtonCallback(() -> { + container.removeView(magnifierView); + magnifierView = null; + }); + container.addView(magnifierView); + } + drawerLayout.closeDrawers(); + break; case R.id.main_menu_touchpad_help: showTouchpadHelpDialog(); break; @@ -262,36 +303,40 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat boolean containerDataChanged = false; if (!container.getExtra("appVersion").equals(appVersion) || !container.getExtra("imgVersion").equals(imgVersion)) { - File pulseaudioDir = new File(getFilesDir(), "pulseaudio"); - TarZstdUtils.extract(this, "patches.tzst", rootDir, onExtractFileListener); - TarZstdUtils.extract(this, "pulseaudio.tzst", pulseaudioDir); - WineUtils.applyRegistryTweaks(this); + FileUtils.delete(new File(rootDir, "/opt/apps")); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "imagefs_patches.tzst", rootDir, onExtractFileListener); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "pulseaudio.tzst", new File(getFilesDir(), "pulseaudio")); + WineUtils.applySystemTweaks(this, wineInfo, firstTimeBoot); container.putExtra("appVersion", appVersion); container.putExtra("imgVersion", imgVersion); + container.putExtra("graphicsDriver", null); + container.putExtra("desktopTheme", null); SettingsFragment.resetBox86_64Version(this); containerDataChanged = true; } - String dxwrapper = shortcut != null ? shortcut.getExtra("dxwrapper", container.getDXWrapper()) : container.getDXWrapper(); + String dxwrapper = this.dxwrapper; + if (dxwrapper.equals("dxvk")) dxwrapper = "dxvk-"+dxwrapperConfig.get("version"); + if (!dxwrapper.equals(container.getExtra("dxwrapper"))) { extractDXWrapperFiles(dxwrapper); container.putExtra("dxwrapper", dxwrapper); containerDataChanged = true; } + if (dxwrapper.equals("cnc-ddraw")) envVars.put("CNC_DDRAW_CONFIG_FILE", "C:\\ProgramData\\cnc-ddraw\\ddraw.ini"); - String dxcomponents = shortcut != null ? shortcut.getExtra("dxcomponents", container.getDXComponents()) : container.getDXComponents(); - if (!dxcomponents.equals(container.getExtra("dxcomponents"))) { - extractDXComponentFiles(); - container.putExtra("dxcomponents", dxcomponents); + String wincomponents = shortcut != null ? shortcut.getExtra("wincomponents", container.getWinComponents()) : container.getWinComponents(); + if (!wincomponents.equals(container.getExtra("wincomponents"))) { + extractWinComponentFiles(); + container.putExtra("wincomponents", wincomponents); containerDataChanged = true; } String desktopTheme = container.getDesktopTheme(); - if (!desktopTheme.equals(container.getExtra("desktopTheme"))) { - String[] desktopThemeArray = desktopTheme.split(","); - WineThemeManager.apply(this, WineThemeManager.Theme.valueOf(desktopThemeArray[0]), desktopThemeArray[1]); - container.putExtra("desktopTheme", desktopTheme); + if (!(desktopTheme+","+xServer.screenInfo).equals(container.getExtra("desktopTheme"))) { + WineThemeManager.apply(this, new WineThemeManager.ThemeInfo(desktopTheme), xServer.screenInfo); + container.putExtra("desktopTheme", desktopTheme+","+xServer.screenInfo); } if (containerDataChanged) container.saveData(); @@ -304,7 +349,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat envVars.put("MESA_DEBUG", "silent"); envVars.put("MESA_NO_ERROR", "1"); envVars.put("WINEPREFIX", ImageFs.WINEPREFIX); - envVars.put("WINEESYNC", "1"); if (MainActivity.DEBUG_LEVEL <= 1) envVars.put("WINEDEBUG", "-all"); String rootPath = imageFs.getRootDir().getPath(); @@ -315,8 +359,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat if (container != null) { if (container.isStopServicesOnStartup()) winHandler.killProcess("services.exe"); - String wineLoader = wineInfo.isWin64() ? "wine64" : "wine"; - String guestExecutable = wineLoader+" explorer /desktop=shell,"+xServer.screenInfo+" "+getWineStartCommand(); + String guestExecutable = wineInfo.binName()+" explorer /desktop=shell,"+xServer.screenInfo+" "+getWineStartCommand(); guestProgramLauncherComponent.setGuestExecutable(guestExecutable); if (container.isShowFPS()) { @@ -325,11 +368,12 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat } envVars.putAll(container.getEnvVars()); if (shortcut != null) envVars.putAll(shortcut.getExtra("envVars")); + if (!envVars.has("WINEESYNC")) envVars.put("WINEESYNC", "1"); + if (graphicsDriver.equals("virgl")) envVars.put("WINEESYNC", "0"); ArrayList bindingPaths = new ArrayList<>(); for (String[] drive : container.drivesIterator()) bindingPaths.add(drive[1]); guestProgramLauncherComponent.setBindingPaths(bindingPaths.toArray(new String[0])); - guestProgramLauncherComponent.setCpuList(container.getCPUList()); guestProgramLauncherComponent.setBox86Preset(shortcut != null ? shortcut.getExtra("box86Preset", container.getBox86Preset()) : container.getBox86Preset()); guestProgramLauncherComponent.setBox64Preset(shortcut != null ? shortcut.getExtra("box64Preset", container.getBox64Preset()) : container.getBox64Preset()); } @@ -361,6 +405,8 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat environment.startEnvironmentComponents(); winHandler.start(); + envVars.clear(); + dxwrapperConfig = null; } private void setupUI() { @@ -392,6 +438,14 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat inputControlsView.setVisibility(View.GONE); container.addView(inputControlsView); + if (shortcut != null) { + String controlsProfile = shortcut.getExtra("controlsProfile"); + if (!controlsProfile.isEmpty()) { + ControlsProfile profile = inputControlsManager.getProfile(Integer.parseInt(controlsProfile)); + if (profile != null) showInputControls(profile); + } + } + AppUtils.observeSoftKeyboardVisibility(drawerLayout, renderer::setScreenOffsetYRelativeToCursor); } @@ -473,45 +527,52 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat } private void extractGraphicsDriverFiles() { + String cacheId = graphicsDriver; + if (graphicsDriver.equals("turnip")) { + cacheId += "-"+DefaultVersion.TURNIP+"-"+DefaultVersion.ZINK; + } + else if (graphicsDriver.equals("virgl")) { + cacheId += "-"+DefaultVersion.VIRGL; + } + + boolean changed = !cacheId.equals(container.getExtra("graphicsDriver")); File rootDir = imageFs.getRootDir(); - FileUtils.delete(new File(rootDir, "/usr/lib/arm-linux-gnueabihf/libvulkan_freedreno.so")); - FileUtils.delete(new File(rootDir, "/usr/lib/aarch64-linux-gnu/libvulkan_freedreno.so")); - FileUtils.delete(new File(rootDir, "/usr/lib/arm-linux-gnueabihf/libGL.so.1.7.0")); - FileUtils.delete(new File(rootDir, "/usr/lib/aarch64-linux-gnu/libGL.so.1.7.0")); + if (changed) { + FileUtils.delete(new File(rootDir, "/usr/lib/arm-linux-gnueabihf/libvulkan_freedreno.so")); + FileUtils.delete(new File(rootDir, "/usr/lib/aarch64-linux-gnu/libvulkan_freedreno.so")); + FileUtils.delete(new File(rootDir, "/usr/lib/arm-linux-gnueabihf/libGL.so.1.7.0")); + FileUtils.delete(new File(rootDir, "/usr/lib/aarch64-linux-gnu/libGL.so.1.7.0")); + container.putExtra("graphicsDriver", cacheId); + container.saveData(); + } - switch (graphicsDriver) { - case "llvmpipe": - envVars.put("GALLIUM_DRIVER", "llvmpipe"); - TarZstdUtils.extract(this, "graphics_driver/llvmpipe-23.1.6.tzst", rootDir); - break; - case "turnip-zink": - envVars.put("GALLIUM_DRIVER", "zink"); - envVars.put("DXVK_STATE_CACHE_PATH", ImageFs.CACHE_PATH); - envVars.put("DXVK_LOG_LEVEL", "none"); - envVars.put("TU_DEBUG", "noconform"); - envVars.put("MESA_VK_WSI_PRESENT_MODE", "mailbox"); - envVars.put("vblank_mode", "0"); + if (graphicsDriver.equals("turnip")) { + if (dxwrapper.equals("dxvk")) DXVKConfigDialog.setEnvVars(this, dxwrapperConfig, envVars); + envVars.put("GALLIUM_DRIVER", "zink"); + envVars.put("TU_DEBUG", "noconform"); + envVars.put("MESA_VK_WSI_PRESENT_MODE", "mailbox"); + envVars.put("vblank_mode", "0"); - boolean useDRI3 = preferences.getBoolean("use_dri3", true); - if (!useDRI3) { - envVars.put("MESA_VK_WSI_PRESENT_MODE", "immediate"); - envVars.put("MESA_VK_WSI_DEBUG", "sw"); - } + boolean useDRI3 = preferences.getBoolean("use_dri3", true); + if (!useDRI3) { + envVars.put("MESA_VK_WSI_PRESENT_MODE", "immediate"); + envVars.put("MESA_VK_WSI_DEBUG", "sw"); + } - String turnipVersion = preferences.getString("turnip_version", SettingsFragment.getDefaultTurnipVersion(this)); - TarZstdUtils.extract(this, "graphics_driver/turnip-"+turnipVersion+".tzst", rootDir); - TarZstdUtils.extract(this, "graphics_driver/zink-22.2.2.tzst", rootDir); - break; - case "virgl": - envVars.put("GALLIUM_DRIVER", "virpipe"); - envVars.put("VIRGL_NO_READBACK", "true"); - envVars.put("VIRGL_SERVER_PATH", UnixSocketConfig.VIRGL_SERVER_PATH); - envVars.put("MESA_EXTENSION_OVERRIDE", "-GL_EXT_vertex_array_bgra -GL_EXT_texture_sRGB_decode -GL_ARB_ES2_compatibility"); - envVars.put("MESA_GL_VERSION_OVERRIDE", "3.1"); - envVars.put("vblank_mode", "0"); - TarZstdUtils.extract(this, "graphics_driver/virgl-22.1.7.tzst", rootDir); - break; + if (changed) { + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/turnip-"+DefaultVersion.TURNIP+".tzst", rootDir); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/zink-"+DefaultVersion.ZINK+".tzst", rootDir); + } + } + else if (graphicsDriver.equals("virgl")) { + envVars.put("GALLIUM_DRIVER", "virpipe"); + envVars.put("VIRGL_NO_READBACK", "true"); + envVars.put("VIRGL_SERVER_PATH", UnixSocketConfig.VIRGL_SERVER_PATH); + envVars.put("MESA_EXTENSION_OVERRIDE", "-GL_EXT_vertex_array_bgra -GL_EXT_texture_sRGB_decode"); + envVars.put("MESA_GL_VERSION_OVERRIDE", "3.1"); + envVars.put("vblank_mode", "0"); + if (changed) TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "graphics_driver/virgl-"+DefaultVersion.VIRGL+".tzst", rootDir); } } @@ -525,7 +586,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { - return !winHandler.onGenericMotionEvent(event) && super.dispatchGenericMotionEvent(event); + return !winHandler.onGenericMotionEvent(event) && !touchpadView.onExternalMouseEvent(event) && super.dispatchGenericMotionEvent(event); } @Override @@ -555,9 +616,8 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat linkFile.delete(); FileUtils.symlink(".."+FileUtils.toRelativePath(rootDir.getPath(), containerPatternDir.getPath()), linkFile.getPath()); - String wineLoader = wineInfo.isWin64() ? "wine64" : "wine"; GuestProgramLauncherComponent guestProgramLauncherComponent = environment.getComponent(GuestProgramLauncherComponent.class); - guestProgramLauncherComponent.setGuestExecutable(wineLoader+" explorer /desktop=shell,"+Container.DEFAULT_SCREEN_SIZE+" winecfg"); + guestProgramLauncherComponent.setGuestExecutable(wineInfo.binName()+" explorer /desktop=shell,"+Container.DEFAULT_SCREEN_SIZE+" winecfg"); final PreloaderDialog preloaderDialog = new PreloaderDialog(this); guestProgramLauncherComponent.setTerminationCallback((status) -> Executors.newSingleThreadExecutor().execute(() -> { @@ -585,7 +645,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat String suffix = wineInfo.fullVersion()+"-"+wineInfo.getArch(); File containerPatternFile = new File(installedWineDir, "/preinstall/container-pattern-"+suffix+".tzst"); - TarZstdUtils.compress(new File(rootDir, ImageFs.WINEPREFIX), containerPatternFile, MainActivity.CONTAINER_PATTERN_COMPRESSION_LEVEL); + TarCompressorUtils.compress(TarCompressorUtils.Type.ZSTD, new File(rootDir, ImageFs.WINEPREFIX), containerPatternFile, MainActivity.CONTAINER_PATTERN_COMPRESSION_LEVEL); if (!containerPatternFile.renameTo(new File(installedWineDir, containerPatternFile.getName())) || !(new File(wineInfo.path)).renameTo(new File(installedWineDir, wineInfo.identifier()))) { @@ -605,7 +665,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat File rootDir = imageFs.getRootDir(); File windowsDir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/windows"); - if (dxwrapper.equals("original-wined3d")) { + if (dxwrapper.equals("wined3d")) { restoreOriginalDllFiles(dlls); } else if (dxwrapper.equals("cnc-ddraw")) { @@ -614,31 +674,30 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat if (!configFile.isFile()) FileUtils.copy(this, "dxwrapper/cnc-ddraw/ddraw.ini", configFile); File shadersDir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/ProgramData/cnc-ddraw/Shaders"); if (!shadersDir.isDirectory()) FileUtils.copy(this, "dxwrapper/cnc-ddraw/Shaders", shadersDir); - TarZstdUtils.extract(this, "dxwrapper/cnc-ddraw/ddraw.tzst", windowsDir, onExtractFileListener); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/cnc-ddraw/ddraw.tzst", windowsDir, onExtractFileListener); } else { restoreOriginalDllFiles("ddraw.dll"); - TarZstdUtils.extract(this, "dxwrapper/"+dxwrapper+".tzst", windowsDir, onExtractFileListener); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "dxwrapper/"+dxwrapper+".tzst", windowsDir, onExtractFileListener); } } - private void extractDXComponentFiles() { + private void extractWinComponentFiles() { File rootDir = imageFs.getRootDir(); - File dxcomponentsDir = new File(rootDir, "/opt/resources/dxcomponents"); File windowsDir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/windows"); File systemRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/system.reg"); try { - JSONObject dxcomponentsJSONObject = new JSONObject(FileUtils.readString(this, "dxcomponents.json")); + JSONObject wincomponentsJSONObject = new JSONObject(FileUtils.readString(this, "wincomponents/wincomponents.json")); ArrayList dlls = new ArrayList<>(); - String dxcomponents = shortcut != null ? shortcut.getExtra("dxcomponents", container.getDXComponents()) : container.getDXComponents(); + String wincomponents = shortcut != null ? shortcut.getExtra("wincomponents", container.getWinComponents()) : container.getWinComponents(); if (firstTimeBoot) { - for (String[] dxcomponent : Container.dxcomponentsIterator(dxcomponents)) { - JSONArray libNames = dxcomponentsJSONObject.getJSONArray(dxcomponent[0]); - for (int i = 0; i < libNames.length(); i++) { - String libName = libNames.getString(i); - dlls.add(!libName.endsWith(".exe") ? libName+".dll" : libName); + for (String[] wincomponent : new KeyValueSet(wincomponents)) { + JSONArray dlnames = wincomponentsJSONObject.getJSONArray(wincomponent[0]); + for (int i = 0; i < dlnames.length(); i++) { + String dlname = dlnames.getString(i); + dlls.add(!dlname.endsWith(".exe") ? dlname+".dll" : dlname); } } @@ -646,21 +705,21 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat dlls.clear(); } - Iterator oldDXComponentsIter = Container.dxcomponentsIterator(container.getExtra("dxcomponents", dxcomponents)).iterator(); + Iterator oldWinComponentsIter = new KeyValueSet(container.getExtra("wincomponents", Container.FALLBACK_WINCOMPONENTS)).iterator(); - for (String[] dxcomponent : Container.dxcomponentsIterator(dxcomponents)) { - if (dxcomponent[1].equals(oldDXComponentsIter.next()[1])) continue; - String identifier = dxcomponent[0]; - boolean useNative = dxcomponent[1].equals("1"); + for (String[] wincomponent : new KeyValueSet(wincomponents)) { + if (wincomponent[1].equals(oldWinComponentsIter.next()[1])) continue; + String identifier = wincomponent[0]; + boolean useNative = wincomponent[1].equals("1"); if (useNative) { - TarZstdUtils.extract(new File(dxcomponentsDir, identifier+".tzst"), windowsDir, onExtractFileListener); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, this, "wincomponents/"+identifier+".tzst", windowsDir, onExtractFileListener); } else { - JSONArray libNames = dxcomponentsJSONObject.getJSONArray(identifier); - for (int i = 0; i < libNames.length(); i++) { - String libName = libNames.getString(i); - dlls.add(!libName.endsWith(".exe") ? libName+".dll" : libName); + JSONArray dlnames = wincomponentsJSONObject.getJSONArray(identifier); + for (int i = 0; i < dlnames.length(); i++) { + String dlname = dlnames.getString(i); + dlls.add(!dlname.endsWith(".exe") ? dlname+".dll" : dlname); } } @@ -687,7 +746,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat } if (!dlls.isEmpty()) restoreOriginalDllFiles(dlls.toArray(new String[0])); - WineUtils.overrideDXComponentDlls(this, container, dxcomponents); + WineUtils.overrideWinComponentDlls(this, container, wincomponents); } catch (JSONException e) {} } @@ -713,13 +772,11 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat if (filesCopied == dlls.length) return; } - File containerPatternFile = containerManager.getContainerPatternFile(container.getWineVersion()); - TarZstdUtils.extract(containerPatternFile, container.getRootDir(), (destination, entryName) -> { - if (entryName.contains("system32") || entryName.contains("syswow64")) { + containerManager.extractContainerPatternFile(container.getWineVersion(), container.getRootDir(), (file, size) -> { + String name = file.getName(); + if (name.contains("system32/") || name.contains("syswow64/")) { for (String dll : dlls) { - if (entryName.endsWith("system32/"+dll) || entryName.endsWith("syswow64/"+dll)) { - return new File(destination, entryName); - } + if (name.endsWith("system32/"+dll) || name.endsWith("syswow64/"+dll)) return file; } } return null; @@ -752,6 +809,10 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat FileUtils.clear(tempDir); String args = ""; + + String cpuList = container.getCPUList(); + if (!cpuList.isEmpty()) args += "/affinity "+ProcessHelper.getAffinityMask(cpuList)+" "; + if (shortcut != null) { String execArgs = shortcut.getExtra("execArgs"); execArgs = !execArgs.isEmpty() ? " "+execArgs : ""; diff --git a/app/src/main/java/com/winlator/box86_64/Box86_64Preset.java b/app/src/main/java/com/winlator/box86_64/Box86_64Preset.java index 4471c03..a6dd1c8 100644 --- a/app/src/main/java/com/winlator/box86_64/Box86_64Preset.java +++ b/app/src/main/java/com/winlator/box86_64/Box86_64Preset.java @@ -3,6 +3,7 @@ package com.winlator.box86_64; import androidx.annotation.NonNull; public class Box86_64Preset { + public static final String STABILITY = "STABILITY"; public static final String COMPATIBILITY = "COMPATIBILITY"; public static final String INTERMEDIATE = "INTERMEDIATE"; public static final String PERFORMANCE = "PERFORMANCE"; diff --git a/app/src/main/java/com/winlator/box86_64/Box86_64PresetManager.java b/app/src/main/java/com/winlator/box86_64/Box86_64PresetManager.java index 4dd56d4..691a76d 100644 --- a/app/src/main/java/com/winlator/box86_64/Box86_64PresetManager.java +++ b/app/src/main/java/com/winlator/box86_64/Box86_64PresetManager.java @@ -2,6 +2,7 @@ package com.winlator.box86_64; import android.content.Context; import android.content.SharedPreferences; +import android.os.Environment; import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.SpinnerAdapter; @@ -11,34 +12,47 @@ import androidx.preference.PreferenceManager; import com.winlator.R; import com.winlator.SettingsFragment; import com.winlator.core.EnvVars; +import com.winlator.core.FileUtils; +import com.winlator.core.ImageUtils; +import com.winlator.xenvironment.ImageFs; +import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.Locale; public abstract class Box86_64PresetManager { public static EnvVars getEnvVars(String prefix, Context context, String id) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); String ucPrefix = prefix.toUpperCase(Locale.ENGLISH); EnvVars envVars = new EnvVars(); - if (id.equals(Box86_64Preset.COMPATIBILITY)) { + if (id.equals(Box86_64Preset.STABILITY)) { + envVars.put(ucPrefix+"_DYNAREC_SAFEFLAGS", "2"); + envVars.put(ucPrefix+"_DYNAREC_FASTNAN", "0"); + envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "0"); + envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "1"); + envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "0"); + envVars.put(ucPrefix+"_DYNAREC_CALLRET", "0"); + envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "2"); + envVars.put(ucPrefix+"_DYNAREC_WAIT", "0"); + } + else if (id.equals(Box86_64Preset.COMPATIBILITY)) { envVars.put(ucPrefix+"_DYNAREC_SAFEFLAGS", "2"); envVars.put(ucPrefix+"_DYNAREC_FASTNAN", "0"); envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "0"); envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "1"); envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "0"); envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "1"); - envVars.put(ucPrefix+"_DYNAREC_WAIT", "0"); + envVars.put(ucPrefix+"_DYNAREC_WAIT", "1"); } else if (id.equals(Box86_64Preset.INTERMEDIATE)) { envVars.put(ucPrefix+"_DYNAREC_SAFEFLAGS", "2"); envVars.put(ucPrefix+"_DYNAREC_FASTNAN", "1"); envVars.put(ucPrefix+"_DYNAREC_FASTROUND", "1"); envVars.put(ucPrefix+"_DYNAREC_X87DOUBLE", "1"); - envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "2"); + envVars.put(ucPrefix+"_DYNAREC_BIGBLOCK", "1"); envVars.put(ucPrefix+"_DYNAREC_STRONGMEM", "0"); - envVars.put(ucPrefix+"_DYNAREC_FORWARD", "512"); + envVars.put(ucPrefix+"_DYNAREC_FORWARD", "128"); envVars.put(ucPrefix+"_DYNAREC_WAIT", "1"); } else if (id.equals(Box86_64Preset.PERFORMANCE)) { @@ -60,20 +74,15 @@ public abstract class Box86_64PresetManager { } } - String box64Version = preferences.getString("box64_version", SettingsFragment.DEFAULT_BOX64_VERSION); - if (box64Version.equals("0.2.2")) { - envVars.remove("BOX64_DYNAREC_BIGBLOCK"); - envVars.remove("BOX64_DYNAREC_STRONGMEM"); - } - return envVars; } public static ArrayList getPresets(String prefix, Context context) { ArrayList presets = new ArrayList<>(); + presets.add(new Box86_64Preset(Box86_64Preset.STABILITY, context.getString(R.string.stability))); presets.add(new Box86_64Preset(Box86_64Preset.COMPATIBILITY, context.getString(R.string.compatibility))); presets.add(new Box86_64Preset(Box86_64Preset.INTERMEDIATE, context.getString(R.string.intermediate))); - presets.add(new Box86_64Preset(Box86_64Preset.PERFORMANCE, context.getString(R.string.performance_unstable))); + presets.add(new Box86_64Preset(Box86_64Preset.PERFORMANCE, context.getString(R.string.performance))); for (String[] preset : customPresetsIterator(prefix, context)) presets.add(new Box86_64Preset(preset[0], preset[1])); return presets; } diff --git a/app/src/main/java/com/winlator/container/Container.java b/app/src/main/java/com/winlator/container/Container.java index f7bdab2..1c6fd6a 100644 --- a/app/src/main/java/com/winlator/container/Container.java +++ b/app/src/main/java/com/winlator/container/Container.java @@ -3,7 +3,10 @@ package com.winlator.container; import android.os.Environment; import com.winlator.box86_64.Box86_64Preset; +import com.winlator.core.AppUtils; +import com.winlator.core.EnvVars; import com.winlator.core.FileUtils; +import com.winlator.core.KeyValueSet; import com.winlator.core.WineInfo; import com.winlator.core.WineThemeManager; import com.winlator.xenvironment.ImageFs; @@ -15,12 +18,13 @@ import java.io.File; import java.util.Iterator; public class Container { - public static final String DEFAULT_ENV_VARS = "ZINK_DESCRIPTORS=lazy ZINK_CONTEXT_THREADED=false ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true"; + public static final String DEFAULT_ENV_VARS = "ZINK_DESCRIPTORS=lazy ZINK_CONTEXT_THREADED=true ZINK_DEBUG=compact MESA_SHADER_CACHE_DISABLE=false MESA_SHADER_CACHE_MAX_SIZE=512MB mesa_glthread=true WINEESYNC=1"; public static final String DEFAULT_SCREEN_SIZE = "800x600"; - public static final String DEFAULT_GRAPHICS_DRIVER = "turnip-zink"; + public static final String DEFAULT_GRAPHICS_DRIVER = "turnip"; public static final String DEFAULT_AUDIO_DRIVER = "alsa"; - public static final String DEFAULT_DXWRAPPER = "original-wined3d"; - public static final String DEFAULT_DXCOMPONENTS = "direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0"; + public static final String DEFAULT_DXWRAPPER = "wined3d"; + public static final String DEFAULT_WINCOMPONENTS = "direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0,vcrun2010=1"; + public static final String FALLBACK_WINCOMPONENTS = "direct3d=0,directsound=0,directmusic=0,directshow=0,directplay=0,vcrun2010=0"; public static final String DEFAULT_DRIVES = "D:"+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); public static final byte MAX_DRIVE_LETTERS = 8; public final int id; @@ -29,14 +33,15 @@ public class Container { private String envVars = DEFAULT_ENV_VARS; private String graphicsDriver = DEFAULT_GRAPHICS_DRIVER; private String dxwrapper = DEFAULT_DXWRAPPER; - private String dxcomponents = DEFAULT_DXCOMPONENTS; + private String dxwrapperConfig = ""; + private String wincomponents = DEFAULT_WINCOMPONENTS; private String audioDriver = DEFAULT_AUDIO_DRIVER; private String drives = DEFAULT_DRIVES; private String wineVersion = WineInfo.MAIN_WINE_VERSION.identifier(); private boolean showFPS; - private boolean stopServicesOnStartup; + private boolean stopServicesOnStartup = true; private String cpuList; - private String desktopTheme = WineThemeManager.DEFAULT_THEME+","+WineThemeManager.DEFAULT_BACKGROUND; + private String desktopTheme = WineThemeManager.DEFAULT_DESKTOP_THEME; private String box86Preset = Box86_64Preset.COMPATIBILITY; private String box64Preset = Box86_64Preset.COMPATIBILITY; private File rootDir; @@ -87,6 +92,14 @@ public class Container { this.dxwrapper = dxwrapper; } + public String getDXWrapperConfig() { + return dxwrapperConfig; + } + + public void setDXWrapperConfig(String dxwrapperConfig) { + this.dxwrapperConfig = dxwrapperConfig != null ? dxwrapperConfig : ""; + } + public String getAudioDriver() { return audioDriver; } @@ -95,12 +108,12 @@ public class Container { this.audioDriver = audioDriver; } - public String getDXComponents() { - return dxcomponents; + public String getWinComponents() { + return wincomponents; } - public void setDXComponents(String dxcomponents) { - this.dxcomponents = dxcomponents; + public void setWinComponents(String wincomponents) { + this.wincomponents = wincomponents; } public String getDrives() { @@ -243,33 +256,6 @@ public class Container { }; } - public Iterable dxcomponentsIterator() { - return dxcomponentsIterator(dxcomponents); - } - - public static Iterable dxcomponentsIterator(final String dxcomponents) { - final int[] start = {0}; - final int[] end = {dxcomponents.indexOf(",")}; - final String[] item = new String[2]; - return () -> new Iterator() { - @Override - public boolean hasNext() { - return start[0] < end[0]; - } - - @Override - public String[] next() { - int index = dxcomponents.indexOf("=", start[0]); - item[0] = dxcomponents.substring(start[0], index); - item[1] = dxcomponents.substring(index+1, end[0]); - start[0] = end[0]+1; - end[0] = dxcomponents.indexOf(",", start[0]); - if (end[0] == -1) end[0] = dxcomponents.length(); - return item; - } - }; - } - public void saveData() { try { JSONObject data = new JSONObject(); @@ -280,8 +266,9 @@ public class Container { data.put("cpuList", cpuList); data.put("graphicsDriver", graphicsDriver); data.put("dxwrapper", dxwrapper); + if (!dxwrapperConfig.isEmpty()) data.put("dxwrapperConfig", dxwrapperConfig); data.put("audioDriver", audioDriver); - data.put("dxcomponents", dxcomponents); + data.put("wincomponents", wincomponents); data.put("drives", drives); data.put("showFPS", showFPS); data.put("stopServicesOnStartup", stopServicesOnStartup); @@ -298,4 +285,130 @@ public class Container { } catch (JSONException e) {} } + + public void loadData(JSONObject data) throws JSONException { + wineVersion = WineInfo.MAIN_WINE_VERSION.identifier(); + dxwrapperConfig = ""; + checkObsoleteOrMissingProperties(data); + + for (Iterator it = data.keys(); it.hasNext(); ) { + String key = it.next(); + switch (key) { + case "name" : + setName(data.getString(key)); + break; + case "screenSize" : + setScreenSize(data.getString(key)); + break; + case "envVars" : + setEnvVars(data.getString(key)); + break; + case "cpuList" : + setCPUList(data.getString(key)); + break; + case "graphicsDriver" : + setGraphicsDriver(data.getString(key)); + break; + case "wincomponents" : + setWinComponents(data.getString(key)); + break; + case "dxwrapper" : + setDXWrapper(data.getString(key)); + break; + case "dxwrapperConfig" : + setDXWrapperConfig(data.getString(key)); + break; + case "drives" : + setDrives(data.getString(key)); + break; + case "showFPS" : + setShowFPS(data.getBoolean(key)); + break; + case "stopServicesOnStartup" : + setStopServicesOnStartup(data.getBoolean(key)); + break; + case "extraData" : { + JSONObject extraData = data.getJSONObject(key); + checkObsoleteOrMissingProperties(extraData); + setExtraData(extraData); + break; + } + case "wineVersion" : + setWineVersion(data.getString(key)); + break; + case "box86Preset" : + setBox86Preset(data.getString(key)); + break; + case "box64Preset" : + setBox64Preset(data.getString(key)); + break; + case "audioDriver" : + setAudioDriver(data.getString(key)); + break; + case "desktopTheme" : + setDesktopTheme(data.getString(key)); + break; + } + } + } + + public static void checkObsoleteOrMissingProperties(JSONObject data) { + try { + if (data.has("dxcomponents")) { + data.put("wincomponents", data.getString("dxcomponents")); + data.remove("dxcomponents"); + } + + if (data.has("dxwrapper")) { + String dxwrapper = data.getString("dxwrapper"); + if (dxwrapper.equals("original-wined3d")) { + data.put("dxwrapper", DEFAULT_DXWRAPPER); + } + else if (dxwrapper.startsWith("d8vk-") || dxwrapper.startsWith("dxvk-")) { + data.put("dxwrapper", dxwrapper.substring(0, dxwrapper.indexOf("-"))); + } + } + + if (data.has("graphicsDriver")) { + String graphicsDriver = data.getString("graphicsDriver"); + if (graphicsDriver.equals("turnip-zink")) { + data.put("graphicsDriver", "turnip"); + } + else if (graphicsDriver.equals("llvmpipe")) { + data.put("graphicsDriver", "virgl"); + } + } + + if (data.has("envVars") && data.has("extraData")) { + JSONObject extraData = data.getJSONObject("extraData"); + int appVersion = Integer.parseInt(extraData.optString("appVersion", "0")); + if (appVersion < 12) { + EnvVars defaultEnvVars = new EnvVars(DEFAULT_ENV_VARS); + EnvVars envVars = new EnvVars(data.getString("envVars")); + for (String name : defaultEnvVars) if (!envVars.has(name)) envVars.put(name, defaultEnvVars.get(name)); + data.put("envVars", envVars.toString()); + } + } + + KeyValueSet wincomponents1 = new KeyValueSet(DEFAULT_WINCOMPONENTS); + KeyValueSet wincomponents2 = new KeyValueSet(data.getString("wincomponents")); + String result = ""; + + for (String[] wincomponent1 : wincomponents1) { + String value = wincomponent1[1]; + + for (String[] wincomponent2 : wincomponents2) { + if (wincomponent1[0].equals(wincomponent2[0])) { + value = wincomponent2[1]; + break; + } + } + + result += (!result.isEmpty() ? "," : "")+wincomponent1[0]+"="+value; + } + + data.put("wincomponents", result); + } + catch (JSONException e) {} + } } diff --git a/app/src/main/java/com/winlator/container/ContainerManager.java b/app/src/main/java/com/winlator/container/ContainerManager.java index 6798010..6f14055 100644 --- a/app/src/main/java/com/winlator/container/ContainerManager.java +++ b/app/src/main/java/com/winlator/container/ContainerManager.java @@ -6,10 +6,12 @@ import android.os.Handler; import com.winlator.R; import com.winlator.core.Callback; import com.winlator.core.FileUtils; -import com.winlator.core.TarZstdUtils; +import com.winlator.core.OnExtractFileListener; +import com.winlator.core.TarCompressorUtils; import com.winlator.core.WineInfo; import com.winlator.xenvironment.ImageFs; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -47,26 +49,8 @@ public class ContainerManager { if (file.getName().startsWith(ImageFs.USER+"-")) { Container container = new Container(Integer.parseInt(file.getName().replace(ImageFs.USER+"-", ""))); container.setRootDir(new File(homeDir, ImageFs.USER+"-"+container.id)); - JSONObject data = new JSONObject(FileUtils.readString(container.getConfigFile())); - container.setName(data.getString("name")); - container.setScreenSize(data.getString("screenSize")); - container.setEnvVars(data.getString("envVars")); - container.setCPUList(data.getString("cpuList")); - container.setGraphicsDriver(data.getString("graphicsDriver")); - container.setDXWrapper(data.getString("dxwrapper")); - container.setDXComponents(data.getString("dxcomponents")); - container.setDrives(data.getString("drives")); - container.setShowFPS(data.getBoolean("showFPS")); - container.setStopServicesOnStartup(data.optBoolean("stopServicesOnStartup")); - - if (data.has("extraData")) container.setExtraData(data.getJSONObject("extraData")); - if (data.has("wineVersion")) container.setWineVersion(data.getString("wineVersion")); - if (data.has("box86Preset")) container.setBox86Preset(data.getString("box86Preset")); - if (data.has("box64Preset")) container.setBox64Preset(data.getString("box64Preset")); - if (data.has("audioDriver")) container.setAudioDriver(data.getString("audioDriver")); - if (data.has("desktopTheme")) container.setDesktopTheme(data.getString("desktopTheme")); - + container.loadData(data); containers.add(container); maxContainerId = Math.max(maxContainerId, container.id); } @@ -118,25 +102,12 @@ public class ContainerManager { Container container = new Container(id); container.setRootDir(containerDir); - container.setName(data.getString("name")); - container.setScreenSize(data.getString("screenSize")); - container.setEnvVars(data.getString("envVars")); - container.setCPUList(data.getString("cpuList")); - container.setGraphicsDriver(data.getString("graphicsDriver")); - container.setDXWrapper(data.getString("dxwrapper")); - container.setAudioDriver(data.getString("audioDriver")); - container.setDXComponents(data.getString("dxcomponents")); - container.setDrives(data.getString("drives")); - container.setShowFPS(data.getBoolean("showFPS")); - container.setStopServicesOnStartup(data.getBoolean("stopServicesOnStartup")); - container.setBox86Preset(data.getString("box86Preset")); - container.setBox64Preset(data.getString("box64Preset")); - container.setDesktopTheme(data.getString("desktopTheme")); + container.loadData(data); boolean isMainWineVersion = !data.has("wineVersion") || data.getString("wineVersion").equals(WineInfo.MAIN_WINE_VERSION.identifier()); if (!isMainWineVersion) container.setWineVersion(data.getString("wineVersion")); - if (!TarZstdUtils.extract(getContainerPatternFile(container.getWineVersion()), containerDir)) { + if (!extractContainerPatternFile(container.getWineVersion(), containerDir, null)) { FileUtils.delete(containerDir); return null; } @@ -169,8 +140,9 @@ public class ContainerManager { dstContainer.setCPUList(srcContainer.getCPUList()); dstContainer.setGraphicsDriver(srcContainer.getGraphicsDriver()); dstContainer.setDXWrapper(srcContainer.getDXWrapper()); + dstContainer.setDXWrapperConfig(srcContainer.getDXWrapperConfig()); dstContainer.setAudioDriver(srcContainer.getAudioDriver()); - dstContainer.setDXComponents(srcContainer.getDXComponents()); + dstContainer.setWinComponents(srcContainer.getWinComponents()); dstContainer.setDrives(srcContainer.getDrives()); dstContainer.setShowFPS(srcContainer.isShowFPS()); dstContainer.setStopServicesOnStartup(srcContainer.isStopServicesOnStartup()); @@ -212,16 +184,44 @@ public class ContainerManager { return null; } - public File getContainerPatternFile(String wineVersion) { + private void extractCommonDlls(String srcName, String dstName, JSONObject commonDlls, File containerDir, OnExtractFileListener onExtractFileListener) throws JSONException { + File srcDir = new File(ImageFs.find(context).getRootDir(), "/opt/wine/lib/wine/"+srcName); + JSONArray dlnames = commonDlls.getJSONArray(dstName); + + for (int i = 0; i < dlnames.length(); i++) { + String dlname = dlnames.getString(i); + File dstFile = new File(containerDir, ".wine/drive_c/windows/"+dstName+"/"+dlname); + if (onExtractFileListener != null) { + dstFile = onExtractFileListener.onExtractFile(dstFile, 0); + if (dstFile == null) continue; + } + FileUtils.copy(new File(srcDir, dlname), dstFile); + } + } + + public boolean extractContainerPatternFile(String wineVersion, File containerDir, OnExtractFileListener onExtractFileListener) { if (wineVersion == null || wineVersion.equals(WineInfo.MAIN_WINE_VERSION.identifier())) { - File rootDir = ImageFs.find(context).getRootDir(); - return new File(rootDir, "/opt/container-pattern.tzst"); + boolean result = TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, context, "container_pattern.tzst", containerDir, onExtractFileListener); + + if (result) { + try { + JSONObject commonDlls = new JSONObject(FileUtils.readString(context, "common_dlls.json")); + extractCommonDlls("x86_64-windows", "system32", commonDlls, containerDir, onExtractFileListener); + extractCommonDlls("i386-windows", "syswow64", commonDlls, containerDir, onExtractFileListener); + } + catch (JSONException e) { + return false; + } + } + + return result; } else { File installedWineDir = ImageFs.find(context).getInstalledWineDir(); WineInfo wineInfo = WineInfo.fromIdentifier(context, wineVersion); String suffix = wineInfo.fullVersion()+"-"+wineInfo.getArch(); - return new File(installedWineDir, "container-pattern-"+suffix+".tzst"); + File file = new File(installedWineDir, "container-pattern-"+suffix+".tzst"); + return TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, file, containerDir, onExtractFileListener); } } } diff --git a/app/src/main/java/com/winlator/container/Shortcut.java b/app/src/main/java/com/winlator/container/Shortcut.java index 115667d..eb5a3bf 100644 --- a/app/src/main/java/com/winlator/container/Shortcut.java +++ b/app/src/main/java/com/winlator/container/Shortcut.java @@ -73,6 +73,8 @@ public class Shortcut { this.iconFile = iconFile; this.path = StringUtils.unescape(execArgs.substring(execArgs.lastIndexOf("wine ") + 4)); this.wmClass = wmClass; + + Container.checkObsoleteOrMissingProperties(extraData); } public String getExtra(String name) { diff --git a/app/src/main/java/com/winlator/contentdialog/DXVKConfigDialog.java b/app/src/main/java/com/winlator/contentdialog/DXVKConfigDialog.java new file mode 100644 index 0000000..b1fdb93 --- /dev/null +++ b/app/src/main/java/com/winlator/contentdialog/DXVKConfigDialog.java @@ -0,0 +1,73 @@ +package com.winlator.contentdialog; + +import android.content.Context; +import android.view.View; +import android.widget.Spinner; + +import com.winlator.R; +import com.winlator.core.AppUtils; +import com.winlator.core.DefaultVersion; +import com.winlator.core.EnvVars; +import com.winlator.core.FileUtils; +import com.winlator.core.KeyValueSet; +import com.winlator.core.StringUtils; +import com.winlator.xenvironment.ImageFs; + +import java.io.File; + +public class DXVKConfigDialog extends ContentDialog { + public static final String DEFAULT_CONFIG = "version="+DefaultVersion.DXVK+",framerate=0,maxDeviceMemory=0"; + + public DXVKConfigDialog(View anchor) { + super(anchor.getContext(), R.layout.dxvk_config_dialog); + setIcon(R.drawable.icon_settings); + setTitle("DXVK "+anchor.getContext().getString(R.string.configuration)); + + final Spinner sVersion = findViewById(R.id.SVersion); + final Spinner sFramerate = findViewById(R.id.SFramerate); + final Spinner sMaxDeviceMemory = findViewById(R.id.SMaxDeviceMemory); + + KeyValueSet config = parseConfig(anchor.getTag()); + AppUtils.setSpinnerSelectionFromIdentifier(sVersion, config.get("version")); + AppUtils.setSpinnerSelectionFromIdentifier(sFramerate, config.get("framerate")); + AppUtils.setSpinnerSelectionFromNumber(sMaxDeviceMemory, config.get("maxDeviceMemory")); + + setOnConfirmCallback(() -> { + config.put("version", StringUtils.parseNumber(sVersion.getSelectedItem())); + config.put("framerate", StringUtils.parseNumber(sFramerate.getSelectedItem())); + config.put("maxDeviceMemory", StringUtils.parseNumber(sMaxDeviceMemory.getSelectedItem())); + anchor.setTag(config.toString()); + }); + } + + public static KeyValueSet parseConfig(Object config) { + String data = config != null && !config.toString().isEmpty() ? config.toString() : DEFAULT_CONFIG; + return new KeyValueSet(data); + } + + public static void setEnvVars(Context context, KeyValueSet config, EnvVars envVars) { + envVars.put("DXVK_STATE_CACHE_PATH", ImageFs.CACHE_PATH); + envVars.put("DXVK_LOG_LEVEL", "none"); + + File rootDir = ImageFs.find(context).getRootDir(); + File dxvkConfigFile = new File(rootDir, ImageFs.CONFIG_PATH+"/dxvk.conf"); + + String content = ""; + String maxDeviceMemory = config.get("maxDeviceMemory"); + if (!maxDeviceMemory.isEmpty() && !maxDeviceMemory.equals("0")) { + content += "dxgi.maxDeviceMemory = "+maxDeviceMemory+"\n"; + content += "dxgi.maxSharedMemory = "+maxDeviceMemory+"\n"; + } + + String framerate = config.get("framerate"); + if (!framerate.isEmpty() && !framerate.equals("0")) { + content += "dxgi.maxFrameRate = "+framerate+"\n"; + content += "d3d9.maxFrameRate = "+framerate+"\n"; + } + + FileUtils.delete(dxvkConfigFile); + if (!content.isEmpty() && FileUtils.writeString(dxvkConfigFile, content)) { + envVars.put("DXVK_CONFIG_FILE", ImageFs.CONFIG_PATH+"/dxvk.conf"); + } + } +} diff --git a/app/src/main/java/com/winlator/contentdialog/ShortcutSettingsDialog.java b/app/src/main/java/com/winlator/contentdialog/ShortcutSettingsDialog.java index ebbb4aa..ec4d3df 100644 --- a/app/src/main/java/com/winlator/contentdialog/ShortcutSettingsDialog.java +++ b/app/src/main/java/com/winlator/contentdialog/ShortcutSettingsDialog.java @@ -14,6 +14,7 @@ import android.widget.PopupMenu; import android.widget.Spinner; import android.widget.TextView; +import com.winlator.ContainerDetailFragment; import com.winlator.R; import com.winlator.ShortcutsFragment; import com.winlator.box86_64.Box86_64PresetManager; @@ -23,13 +24,17 @@ import com.winlator.core.AppUtils; import com.winlator.core.ArrayUtils; import com.winlator.core.EnvVars; import com.winlator.core.StringUtils; +import com.winlator.inputcontrols.ControlsProfile; +import com.winlator.inputcontrols.InputControlsManager; import com.winlator.winhandler.WinHandler; import java.io.File; +import java.util.ArrayList; public class ShortcutSettingsDialog extends ContentDialog { private final ShortcutsFragment fragment; private final Shortcut shortcut; + private InputControlsManager inputControlsManager; public ShortcutSettingsDialog(ShortcutsFragment fragment, Shortcut shortcut) { super(fragment.getContext(), R.layout.shortcut_settings_dialog); @@ -43,36 +48,31 @@ public class ShortcutSettingsDialog extends ContentDialog { private void createContentView() { final Context context = fragment.getContext(); - Resources resources = context.getResources(); + inputControlsManager = new InputControlsManager(context); LinearLayout llContent = findViewById(R.id.LLContent); llContent.getLayoutParams().width = AppUtils.getPreferredDialogWidth(context); - String[] defaultItem = new String[]{"-- "+context.getString(R.string._default)+" --"}; - final EditText etName = findViewById(R.id.ETName); etName.setText(shortcut.name); final EditText etExecArgs = findViewById(R.id.ETExecArgs); etExecArgs.setText(shortcut.getExtra("execArgs")); - String[] screenSizeEntries = ArrayUtils.concat(defaultItem, resources.getStringArray(R.array.screen_size_entries)); - loadScreenSizeSpinner(shortcut.getExtra("screenSize"), screenSizeEntries); + ContainerDetailFragment.loadScreenSizeSpinner(getContentView(), shortcut.getExtra("screenSize", shortcut.container.getScreenSize())); - String[] graphicsDriverEntries = ArrayUtils.concat(defaultItem, resources.getStringArray(R.array.graphics_driver_entries)); final Spinner sGraphicsDriver = findViewById(R.id.SGraphicsDriver); - sGraphicsDriver.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, graphicsDriverEntries)); - AppUtils.setSpinnerSelectionFromIdentifier(sGraphicsDriver, shortcut.getExtra("graphicsDriver")); - - String[] dxwrapperEntries = ArrayUtils.concat(defaultItem, resources.getStringArray(R.array.dxwrapper_entries)); final Spinner sDXWrapper = findViewById(R.id.SDXWrapper); - sDXWrapper.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, dxwrapperEntries)); - AppUtils.setSpinnerSelectionFromIdentifier(sDXWrapper, shortcut.getExtra("dxwrapper")); + + final View vDXWrapperConfig = findViewById(R.id.BTDXWrapperConfig); + vDXWrapperConfig.setTag(shortcut.getExtra("dxwrapperConfig", shortcut.container.getDXWrapperConfig())); + + ContainerDetailFragment.setupDXWrapperSpinner(sDXWrapper, vDXWrapperConfig); + ContainerDetailFragment.loadGraphicsDriverSpinner(sGraphicsDriver, sDXWrapper, + shortcut.getExtra("graphicsDriver", shortcut.container.getGraphicsDriver()), shortcut.getExtra("dxwrapper", shortcut.container.getDXWrapper())); findViewById(R.id.BTHelpDXWrapper).setOnClickListener((v) -> AppUtils.showHelpBox(context, v, R.string.dxwrapper_help_content)); - String[] audioDriverEntries = ArrayUtils.concat(defaultItem, resources.getStringArray(R.array.audio_driver_entries)); final Spinner sAudioDriver = findViewById(R.id.SAudioDriver); - sAudioDriver.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, audioDriverEntries)); AppUtils.setSpinnerSelectionFromIdentifier(sAudioDriver, shortcut.getExtra("audioDriver")); final CheckBox cbForceFullscreen = findViewById(R.id.CBForceFullscreen); @@ -84,14 +84,17 @@ public class ShortcutSettingsDialog extends ContentDialog { final Spinner sBox64Preset = findViewById(R.id.SBox64Preset); Box86_64PresetManager.loadSpinner("box64", sBox64Preset, shortcut.getExtra("box64Preset", shortcut.container.getBox64Preset())); + final Spinner sControlsProfile = findViewById(R.id.SControlsProfile); + loadControlsProfileSpinner(sControlsProfile, shortcut.getExtra("controlsProfile", "0")); + final Spinner sDInputMapperType = findViewById(R.id.SDInputMapperType); sDInputMapperType.setSelection(Byte.parseByte(shortcut.getExtra("dinputMapperType", String.valueOf(WinHandler.DINPUT_MAPPER_TYPE_XINPUT)))); - createDXComponentsTab(); + createWinComponentsTab(); createEnvVarsTab(); findViewById(R.id.BTAddEnvVar).setOnClickListener((v) -> (new AddEnvVarDialog(context, getContentView())).show()); - AppUtils.setupTabLayout(getContentView(), R.id.TabLayout, R.id.LLTabDXComponents, R.id.LLTabEnvVars, R.id.LLTabAdvanced); + AppUtils.setupTabLayout(getContentView(), R.id.TabLayout, R.id.LLTabWinComponents, R.id.LLTabEnvVars, R.id.LLTabAdvanced); findViewById(R.id.BTExtraArgsMenu).setOnClickListener((v) -> { PopupMenu popupMenu = new PopupMenu(context, v); @@ -111,14 +114,20 @@ public class ShortcutSettingsDialog extends ContentDialog { renameShortcut(name); } else { + String graphicsDriver = StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem()); + String dxwrapper = StringUtils.parseIdentifier(sDXWrapper.getSelectedItem()); + String dxwrapperConfig = vDXWrapperConfig.getTag().toString(); + String audioDriver = StringUtils.parseIdentifier(sAudioDriver.getSelectedItem()); + String execArgs = etExecArgs.getText().toString(); shortcut.putExtra("execArgs", !execArgs.isEmpty() ? execArgs : null); shortcut.putExtra("screenSize", getScreenSize()); - shortcut.putExtra("graphicsDriver", sGraphicsDriver.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sGraphicsDriver.getSelectedItem()) : null); - shortcut.putExtra("dxwrapper", sDXWrapper.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sDXWrapper.getSelectedItem()) : null); - shortcut.putExtra("audioDriver", sAudioDriver.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sAudioDriver.getSelectedItem()) : null); + shortcut.putExtra("graphicsDriver", !graphicsDriver.equals(shortcut.container.getGraphicsDriver()) ? graphicsDriver : null); + shortcut.putExtra("dxwrapper", !dxwrapper.equals(shortcut.container.getDXWrapper()) ? dxwrapper : null); + shortcut.putExtra("dxwrapperConfig", !dxwrapperConfig.equals(shortcut.container.getDXWrapperConfig()) ? dxwrapperConfig : null); + shortcut.putExtra("audioDriver", !audioDriver.equals(shortcut.container.getAudioDriver())? audioDriver : null); shortcut.putExtra("forceFullscreen", cbForceFullscreen.isChecked() ? "1" : null); - shortcut.putExtra("dxcomponents", getDXComponents()); + shortcut.putExtra("wincomponents", getWinComponents()); shortcut.putExtra("envVars", getEnvVars()); String box86Preset = Box86_64PresetManager.getSpinnerSelectedId(sBox86Preset); @@ -127,6 +136,9 @@ public class ShortcutSettingsDialog extends ContentDialog { shortcut.putExtra("box64Preset", !box64Preset.equals(shortcut.container.getBox64Preset()) ? box64Preset : null); int dinputMapperType = sDInputMapperType.getSelectedItemPosition(); + ArrayList profiles = inputControlsManager.getProfiles(); + int controlsProfile = sControlsProfile.getSelectedItemPosition() > 0 ? profiles.get(sControlsProfile.getSelectedItemPosition()-1).id : 0; + shortcut.putExtra("controlsProfile", controlsProfile > 0 ? String.valueOf(controlsProfile) : null); shortcut.putExtra("dinputMapperType", dinputMapperType != WinHandler.DINPUT_MAPPER_TYPE_XINPUT ? String.valueOf(dinputMapperType) : null); shortcut.saveData(); } @@ -162,31 +174,6 @@ public class ShortcutSettingsDialog extends ContentDialog { return !envVars.isEmpty() ? envVars.toString() : null; } - private void loadScreenSizeSpinner(String selectedValue, String[] entries) { - final Spinner sScreenSize = findViewById(R.id.SScreenSize); - sScreenSize.setAdapter(new ArrayAdapter<>(fragment.getContext(), android.R.layout.simple_spinner_dropdown_item, entries)); - - final LinearLayout llCustomScreenSize = findViewById(R.id.LLCustomScreenSize); - sScreenSize.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - String value = sScreenSize.getItemAtPosition(position).toString(); - llCustomScreenSize.setVisibility(value.equalsIgnoreCase("custom") ? View.VISIBLE : View.GONE); - } - - @Override - public void onNothingSelected(AdapterView parent) {} - }); - - boolean found = AppUtils.setSpinnerSelectionFromIdentifier(sScreenSize, selectedValue); - if (!found && !selectedValue.isEmpty()) { - AppUtils.setSpinnerSelectionFromValue(sScreenSize, "custom"); - String[] screenSize = selectedValue.split("x"); - ((EditText)findViewById(R.id.ETScreenWidth)).setText(screenSize[0]); - ((EditText)findViewById(R.id.ETScreenHeight)).setText(screenSize[1]); - } - } - private void renameShortcut(String newName) { File parent = shortcut.file.getParentFile(); File newFile = new File(parent, newName+".desktop"); @@ -200,31 +187,31 @@ public class ShortcutSettingsDialog extends ContentDialog { fragment.loadShortcutsList(); } - private String getDXComponents() { - ViewGroup parent = findViewById(R.id.LLTabDXComponents); + private String getWinComponents() { + ViewGroup parent = findViewById(R.id.LLTabWinComponents); int childCount = parent.getChildCount(); - String[] dxcomponents = new String[childCount]; + String[] wincomponents = new String[childCount]; for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); Spinner spinner = child.findViewById(R.id.Spinner); - dxcomponents[i] = child.getTag().toString()+"="+spinner.getSelectedItemPosition(); + wincomponents[i] = child.getTag().toString()+"="+spinner.getSelectedItemPosition(); } - String result = String.join(",", dxcomponents); - return !result.equals(shortcut.container.getDXComponents()) ? result : null; + String result = String.join(",", wincomponents); + return !result.equals(shortcut.container.getWinComponents()) ? result : null; } - private void createDXComponentsTab() { - final String[] dxcomponents = shortcut.getExtra("dxcomponents", shortcut.container.getDXComponents()).split(","); + private void createWinComponentsTab() { + final String[] wincomponents = shortcut.getExtra("wincomponents", shortcut.container.getWinComponents()).split(","); Context context = fragment.getContext(); LayoutInflater inflater = LayoutInflater.from(context); - ViewGroup parent = findViewById(R.id.LLTabDXComponents); + ViewGroup parent = findViewById(R.id.LLTabWinComponents); - for (String dxcomponent : dxcomponents) { - String[] parts = dxcomponent.split("="); - View itemView = inflater.inflate(R.layout.dxcomponent_list_item, parent, false); + for (String wincomponent : wincomponents) { + String[] parts = wincomponent.split("="); + View itemView = inflater.inflate(R.layout.wincomponent_list_item, parent, false); ((TextView)itemView.findViewById(R.id.TextView)).setText(StringUtils.getString(context, parts[0])); ((Spinner)itemView.findViewById(R.id.Spinner)).setSelection(Integer.parseInt(parts[1]), false); itemView.setTag(parts[0]); @@ -252,4 +239,22 @@ public class ShortcutSettingsDialog extends ContentDialog { if (envVars.isEmpty()) emptyTextView.setVisibility(View.VISIBLE); } + + private void loadControlsProfileSpinner(Spinner spinner, String selectedValue) { + final Context context = fragment.getContext(); + final ArrayList profiles = inputControlsManager.getProfiles(true); + ArrayList values = new ArrayList<>(); + values.add(context.getString(R.string.none)); + + int selectedPosition = 0; + int selectedId = Integer.parseInt(selectedValue); + for (int i = 0; i < profiles.size(); i++) { + ControlsProfile profile = profiles.get(i); + if (profile.id == selectedId) selectedPosition = i + 1; + values.add(profile.getName()); + } + + spinner.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, values)); + spinner.setSelection(selectedPosition, false); + } } diff --git a/app/src/main/java/com/winlator/core/AppUtils.java b/app/src/main/java/com/winlator/core/AppUtils.java index 99d9260..7d17eb7 100644 --- a/app/src/main/java/com/winlator/core/AppUtils.java +++ b/app/src/main/java/com/winlator/core/AppUtils.java @@ -261,6 +261,17 @@ public abstract class AppUtils { return false; } + public static boolean setSpinnerSelectionFromNumber(Spinner spinner, String number) { + spinner.setSelection(0, false); + for (int i = 0; i < spinner.getCount(); i++) { + if (StringUtils.parseNumber(spinner.getItemAtPosition(i)).equals(number)) { + spinner.setSelection(i, false); + return true; + } + } + return false; + } + public static void setupTabLayout(final View view, int tabLayoutResId, final int... tabResIds) { final Callback tabSelectedCallback = (position) -> { for (int i = 0; i < tabResIds.length; i++) { diff --git a/app/src/main/java/com/winlator/core/DefaultVersion.java b/app/src/main/java/com/winlator/core/DefaultVersion.java new file mode 100644 index 0000000..a6541ca --- /dev/null +++ b/app/src/main/java/com/winlator/core/DefaultVersion.java @@ -0,0 +1,10 @@ +package com.winlator.core; + +public abstract class DefaultVersion { + public static final String BOX86 = "0.3.0"; + public static final String BOX64 = "0.2.6"; + public static final String TURNIP = "24.0.2"; + public static final String ZINK = "22.2.2"; + public static final String VIRGL = "22.1.7"; + public static final String DXVK = "1.10.3"; +} diff --git a/app/src/main/java/com/winlator/core/DownloadProgressDialog.java b/app/src/main/java/com/winlator/core/DownloadProgressDialog.java index 523d5e2..9d23ed5 100644 --- a/app/src/main/java/com/winlator/core/DownloadProgressDialog.java +++ b/app/src/main/java/com/winlator/core/DownloadProgressDialog.java @@ -2,12 +2,14 @@ package com.winlator.core; import android.app.Activity; import android.app.Dialog; +import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.TextView; import com.google.android.material.progressindicator.CircularProgressIndicator; import com.winlator.R; +import com.winlator.math.Mathf; public class DownloadProgressDialog { private final Activity activity; @@ -32,17 +34,36 @@ public class DownloadProgressDialog { } } - public void show(final Runnable onCancelCallback) { + public void show() { + show(null); + } + + public void show(int textResId) { + show(textResId, null); + } + + public void show(Runnable onCancelCallback) { + show(0, onCancelCallback); + } + + public void show(int textResId, final Runnable onCancelCallback) { if (isShowing()) return; close(); if (dialog == null) create(); + + if (textResId > 0) ((TextView)dialog.findViewById(R.id.TextView)).setText(textResId); + setProgress(0); - dialog.findViewById(R.id.BTCancel).setOnClickListener((v) -> onCancelCallback.run()); + if (onCancelCallback != null) { + dialog.findViewById(R.id.BTCancel).setOnClickListener((v) -> onCancelCallback.run()); + dialog.findViewById(R.id.LLBottomBar).setVisibility(View.VISIBLE); + } dialog.show(); } public void setProgress(int progress) { if (dialog == null) return; + progress = Mathf.clamp(progress, 0, 100); ((CircularProgressIndicator)dialog.findViewById(R.id.CircularProgressIndicator)).setProgress(progress); ((TextView)dialog.findViewById(R.id.TVProgress)).setText(progress+"%"); } @@ -56,6 +77,10 @@ public class DownloadProgressDialog { catch (Exception e) {} } + public void closeOnUiThread() { + activity.runOnUiThread(this::close); + } + public boolean isShowing() { return dialog != null && dialog.isShowing(); } diff --git a/app/src/main/java/com/winlator/core/EnvVars.java b/app/src/main/java/com/winlator/core/EnvVars.java index 1873f2c..2234b60 100644 --- a/app/src/main/java/com/winlator/core/EnvVars.java +++ b/app/src/main/java/com/winlator/core/EnvVars.java @@ -34,7 +34,7 @@ public class EnvVars implements Iterable { } public String get(String name) { - return data.get(name); + return data.getOrDefault(name, ""); } public void remove(String name) { diff --git a/app/src/main/java/com/winlator/core/FileUtils.java b/app/src/main/java/com/winlator/core/FileUtils.java index 8c97841..8d0fb43 100644 --- a/app/src/main/java/com/winlator/core/FileUtils.java +++ b/app/src/main/java/com/winlator/core/FileUtils.java @@ -1,6 +1,7 @@ package com.winlator.core; import android.content.Context; +import android.content.res.AssetFileDescriptor; import android.net.Uri; import android.os.Environment; import android.os.StatFs; @@ -93,14 +94,14 @@ public abstract class FileUtils { return false; } - public static void symlink(File oldFile, File newFile) { - symlink(oldFile.getAbsolutePath(), newFile.getAbsolutePath()); + public static void symlink(File linkTarget, File linkFile) { + symlink(linkTarget.getAbsolutePath(), linkFile.getAbsolutePath()); } - public static void symlink(String oldPath, String newPath) { + public static void symlink(String linkTarget, String linkFile) { try { - (new File(newPath)).delete(); - Os.symlink(oldPath, newPath); + (new File(linkFile)).delete(); + Os.symlink(linkTarget, linkFile); } catch (ErrnoException e) {} } @@ -206,8 +207,8 @@ public abstract class FileUtils { } } - public static List readLines(File file) { - List lines = new ArrayList<>(); + public static ArrayList readLines(File file) { + ArrayList lines = new ArrayList<>(); try (FileInputStream fis = new FileInputStream(file)) { BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); String line; @@ -264,7 +265,19 @@ public abstract class FileUtils { } public static boolean contentEquals(File origin, File target) { - return Arrays.equals(read(origin), read(target)); + if (origin.length() != target.length()) return false; + + try (InputStream inStream1 = new BufferedInputStream(new FileInputStream(origin)); + InputStream inStream2 = new BufferedInputStream(new FileInputStream(target))) { + int data; + while ((data = inStream1.read()) != -1) { + if (data != inStream2.read()) return false; + } + return true; + } + catch (IOException e) { + return false; + } } public static void getSizeAsync(File file, Callback callback) { @@ -297,6 +310,15 @@ public abstract class FileUtils { } } + public static long getSize(Context context, String assetFile) { + try (InputStream inStream = context.getAssets().open(assetFile)) { + return inStream.available(); + } + catch (IOException e) { + return 0; + } + } + public static long getInternalStorageSize() { File dataDir = Environment.getDataDirectory(); StatFs stat = new StatFs(dataDir.getPath()); @@ -330,4 +352,13 @@ public abstract class FileUtils { catch (Exception e) {} return result; } + + public static String readSymlink(File file) { + try { + return Files.readSymbolicLink(file.toPath()).toString(); + } + catch (IOException e) { + return ""; + } + } } diff --git a/app/src/main/java/com/winlator/core/ImageUtils.java b/app/src/main/java/com/winlator/core/ImageUtils.java new file mode 100644 index 0000000..4a5dd53 --- /dev/null +++ b/app/src/main/java/com/winlator/core/ImageUtils.java @@ -0,0 +1,107 @@ +package com.winlator.core; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; + +import androidx.annotation.IntRange; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +public abstract class ImageUtils { + private static int calculateInSampleSize(BitmapFactory.Options options, int maxSize) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + + int reqWidth = width >= height ? maxSize : 0; + int reqHeight = height >= width ? maxSize : 0; + + if (height > reqHeight || width > reqWidth) { + int halfHeight = height / 2; + int halfWidth = width / 2; + while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + return inSampleSize; + } + + public static Bitmap getBitmapFromUri(Context context, Uri uri, BitmapFactory.Options options) { + InputStream is = null; + Bitmap bitmap = null; + try { + is = context.getContentResolver().openInputStream(uri); + if (options != null) { + bitmap = BitmapFactory.decodeStream(is, null, options); + } + else bitmap = BitmapFactory.decodeStream(is); + } + catch (IOException e) { + e.printStackTrace(); + } + finally { + try { + if (is != null) is.close(); + } + catch (IOException e) {} + } + return bitmap; + } + + public static Bitmap getBitmapFromUri(Context context, Uri uri) { + return getBitmapFromUri(context, uri, null); + } + + public static Bitmap getBitmapFromUri(Context context, Uri uri, int maxSize) { + InputStream is = null; + BitmapFactory.Options options = new BitmapFactory.Options(); + + try { + is = context.getContentResolver().openInputStream(uri); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(is, null, options); + int inSampleSize = calculateInSampleSize(options, maxSize); + options.inJustDecodeBounds = false; + options.inSampleSize = inSampleSize; + } + catch (IOException e) { + e.printStackTrace(); + } + finally { + try { + if (is != null) is.close(); + } + catch (IOException e) {} + } + + return getBitmapFromUri(context, uri, options); + } + + public static boolean save(Bitmap bitmap, File output, Bitmap.CompressFormat compressFormat, @IntRange(from = 0, to = 100) int quality) { + FileOutputStream fos = null; + try { + fos = new FileOutputStream(output); + return bitmap.compress(compressFormat, quality, fos); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + try { + if (fos != null) { + fos.flush(); + fos.close(); + } + } + catch (IOException e) { + e.printStackTrace(); + } + } + return false; + } +} diff --git a/app/src/main/java/com/winlator/core/KeyValueSet.java b/app/src/main/java/com/winlator/core/KeyValueSet.java new file mode 100644 index 0000000..2cc1015 --- /dev/null +++ b/app/src/main/java/com/winlator/core/KeyValueSet.java @@ -0,0 +1,74 @@ +package com.winlator.core; + +import androidx.annotation.NonNull; + +import java.util.Iterator; + +public class KeyValueSet implements Iterable { + private String data = ""; + + public KeyValueSet(String data) { + this.data = data != null && !data.isEmpty() ? data : ""; + } + + private int[] indexOfKey(String key) { + int start = 0; + int end = data.indexOf(","); + if (end == -1) end = data.length(); + + while (start < end) { + int index = data.indexOf("=", start); + String currKey = data.substring(start, index); + if (currKey.equals(key)) return new int[]{start, end}; + start = end+1; + end = data.indexOf(",", start); + if (end == -1) end = data.length(); + } + + return null; + } + + public String get(String key) { + for (String[] keyValue : this) if (keyValue[0].equals(key)) return keyValue[1]; + return ""; + } + + public void put(String key, Object value) { + int[] range = indexOfKey(key); + if (range != null) { + data = StringUtils.replace(data, range[0], range[1], key+"="+value); + } + else data = (!data.isEmpty() ? data+"," : "")+key+"="+value; + } + + @NonNull + @Override + public Iterator iterator() { + final int[] start = {0}; + final int[] end = {data.indexOf(",")}; + final String[] item = new String[2]; + return new Iterator() { + @Override + public boolean hasNext() { + return start[0] < end[0]; + } + + @Override + public String[] next() { + int index = data.indexOf("=", start[0]); + item[0] = data.substring(start[0], index); + item[1] = data.substring(index+1, end[0]); + start[0] = end[0]+1; + end[0] = data.indexOf(",", start[0]); + if (end[0] == -1) end[0] = data.length(); + return item; + } + }; + } + + @NonNull + @Override + public String toString() { + return data; + } +} diff --git a/app/src/main/java/com/winlator/core/MSBitmap.java b/app/src/main/java/com/winlator/core/MSBitmap.java new file mode 100644 index 0000000..a21f117 --- /dev/null +++ b/app/src/main/java/com/winlator/core/MSBitmap.java @@ -0,0 +1,76 @@ +package com.winlator.core; + +import android.graphics.Bitmap; +import android.graphics.Color; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public abstract class MSBitmap { + public static boolean create(Bitmap bitmap, File outputFile) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + + int extraBytes = width % 4; + int imageSize = height * (3 * width + extraBytes); + int infoHeaderSize = 40; + int dataOffset = 54; + int bitCount = 24; + int planes = 1; + int compression = 0; + int hr = 0; + int vr = 0; + int colorsUsed = 0; + int colorsImportant = 0; + + ByteBuffer buffer = ByteBuffer.allocate(dataOffset + imageSize).order(ByteOrder.LITTLE_ENDIAN); + + buffer.put(new byte[]{'B', 'M'}); + buffer.putInt(dataOffset + imageSize); + buffer.putInt(0); + buffer.putInt(dataOffset); + + buffer.putInt(infoHeaderSize); + buffer.putInt(width); + buffer.putInt(height); + buffer.putShort((short)planes); + buffer.putShort((short)bitCount); + buffer.putInt(compression); + buffer.putInt(imageSize); + buffer.putInt(hr); + buffer.putInt(vr); + buffer.putInt(colorsUsed); + buffer.putInt(colorsImportant); + + int rowBytes = 3 * width + extraBytes; + for (int y = height - 1, i = 0, j; y >= 0; y--) { + for (int x = 0; x < width; x++) { + j = dataOffset + y * rowBytes + x * 3; + int pixel = pixels[i++]; + buffer.put(j+0, (byte)Color.blue(pixel)); + buffer.put(j+1, (byte)Color.green(pixel)); + buffer.put(j+2, (byte)Color.red(pixel)); + } + + if (extraBytes > 0) { + int fillOffset = dataOffset + y * rowBytes + width * 3; + for (j = fillOffset; j < fillOffset + extraBytes; j++) buffer.put(j, (byte)255); + } + } + + try (FileOutputStream fos = new FileOutputStream(outputFile)) { + fos.write(buffer.array()); + return true; + } + catch (IOException e) { + return false; + } + } +} diff --git a/app/src/main/java/com/winlator/core/MSLink.java b/app/src/main/java/com/winlator/core/MSLink.java index f4df2a3..c3bf403 100644 --- a/app/src/main/java/com/winlator/core/MSLink.java +++ b/app/src/main/java/com/winlator/core/MSLink.java @@ -3,8 +3,27 @@ package com.winlator.core; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; public abstract class MSLink { + public static final byte SW_SHOWNORMAL = 1; + public static final byte SW_SHOWMAXIMIZED = 3; + public static final byte SW_SHOWMINNOACTIVE = 7; + private static final int HasLinkTargetIDList = 1<<0; + private static final int HasArguments = 1<<5; + private static final int HasIconLocation = 1<<6; + private static final int ForceNoLinkInfo = 1<<8; + + public static final class Options { + public String targetPath; + public String cmdArgs; + public String iconLocation; + public int iconIndex; + public int fileSize; + public int showCommand = SW_SHOWNORMAL; + } + private static int charToHexDigit(char chr) { return chr >= 'A' ? chr - 'A' + 10 : chr - '0'; } @@ -40,22 +59,44 @@ public abstract class MSLink { return bytes; } + private static byte[] intToByteArray(int value) { + return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array(); + } + + private static byte[] stringSizePaddedToByteArray(String str) { + ByteBuffer buffer = ByteBuffer.allocate(str.length() + 2).order(ByteOrder.LITTLE_ENDIAN); + buffer.putShort((short)str.length()); + for (int i = 0; i < str.length(); i++) buffer.put((byte)str.charAt(i)); + return buffer.array(); + } + private static byte[] generateIDLIST(byte[] bytes) { - String itemSize = Integer.toHexString(0x10000 + bytes.length + 2).substring(1); - return ArrayUtils.concat(new byte[]{Byte.parseByte(itemSize.substring(2, 4), 16), Byte.parseByte(itemSize.substring(0, 2), 16)}, bytes); + ByteBuffer buffer = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short)(bytes.length + 2)); + return ArrayUtils.concat(buffer.array(), bytes); } public static void createFile(String targetPath, File outputFile) { + Options options = new Options(); + options.targetPath = targetPath; + createFile(options, outputFile); + } + + public static void createFile(Options options, File outputFile) { byte[] HeaderSize = new byte[]{0x4c, 0x00, 0x00, 0x00}; byte[] LinkCLSID = convertCLSIDtoDATA("00021401-0000-0000-c000-000000000046"); - byte[] LinkFlags = new byte[]{0x01, 0x01, 0x00, 0x00}; + + int linkFlags = HasLinkTargetIDList | ForceNoLinkInfo; + if (options.cmdArgs != null && !options.cmdArgs.isEmpty()) linkFlags |= HasArguments; + if (options.iconLocation != null && !options.iconLocation.isEmpty()) linkFlags |= HasIconLocation; + + byte[] LinkFlags = intToByteArray(linkFlags); byte[] FileAttributes, prefixOfTarget; - targetPath = targetPath.replaceAll("/+", "\\\\"); - if (targetPath.endsWith("\\")) { + options.targetPath = options.targetPath.replaceAll("/+", "\\\\"); + if (options.targetPath.endsWith("\\")) { FileAttributes = new byte[]{0x10, 0x00, 0x00, 0x00}; prefixOfTarget = new byte[]{0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - targetPath = targetPath.replaceAll("\\\\+$", ""); + options.targetPath = options.targetPath.replaceAll("\\\\+$", ""); } else { FileAttributes = new byte[]{0x20, 0x00, 0x00, 0x00}; @@ -65,11 +106,11 @@ public abstract class MSLink { byte[] CreationTime, AccessTime, WriteTime; CreationTime = AccessTime = WriteTime = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - byte[] FileSize, IconIndex; - FileSize = IconIndex = new byte[]{0x00, 0x00, 0x00, 0x00}; - byte[] ShowCommand = new byte[]{0x01, 0x00, 0x00, 0x00}; + byte[] FileSize = intToByteArray(options.fileSize); + byte[] IconIndex = intToByteArray(options.iconIndex); + byte[] ShowCommand = intToByteArray(options.showCommand); byte[] Hotkey = new byte[]{0x00, 0x00}; - byte[] Reserved = new byte[]{0x00, 0x00}; + byte[] Reserved1 = new byte[]{0x00, 0x00}; byte[] Reserved2 = new byte[]{0x00, 0x00, 0x00, 0x00}; byte[] Reserved3 = new byte[]{0x00, 0x00, 0x00, 0x00}; @@ -77,17 +118,17 @@ public abstract class MSLink { byte[] CLSIDNetwork = convertCLSIDtoDATA("208d2c60-3aea-1069-a2d7-08002b30309d"); byte[] itemData, prefixRoot, targetRoot, targetLeaf; - if (targetPath.startsWith("\\")) { + if (options.targetPath.startsWith("\\")) { prefixRoot = new byte[]{(byte)0xc3, 0x01, (byte)0x81}; - targetRoot = stringToByteArray(targetPath); - targetLeaf = !targetPath.endsWith("\\") ? stringToByteArray(targetPath.substring(targetPath.lastIndexOf("\\") + 1)) : null; + targetRoot = stringToByteArray(options.targetPath); + targetLeaf = !options.targetPath.endsWith("\\") ? stringToByteArray(options.targetPath.substring(options.targetPath.lastIndexOf("\\") + 1)) : null; itemData = ArrayUtils.concat(new byte[]{0x1f, 0x58}, CLSIDNetwork); } else { prefixRoot = new byte[]{0x2f}; - int index = targetPath.indexOf("\\"); - targetRoot = stringToByteArray(targetPath.substring(0, index+1)); - targetLeaf = stringToByteArray(targetPath.substring(index+1)); + int index = options.targetPath.indexOf("\\"); + targetRoot = stringToByteArray(options.targetPath.substring(0, index+1)); + targetLeaf = stringToByteArray(options.targetPath.substring(index+1)); itemData = ArrayUtils.concat(new byte[]{0x1f, 0x50}, CLSIDComputer); } @@ -100,6 +141,10 @@ public abstract class MSLink { byte[] TerminalID = new byte[]{0x00, 0x00}; + byte[] StringData = new byte[0]; + if ((linkFlags & HasArguments) != 0) StringData = ArrayUtils.concat(StringData, stringSizePaddedToByteArray(options.cmdArgs)); + if ((linkFlags & HasIconLocation) != 0) StringData = ArrayUtils.concat(StringData, stringSizePaddedToByteArray(options.iconLocation)); + try (FileOutputStream os = new FileOutputStream(outputFile)) { os.write(HeaderSize); os.write(LinkCLSID); @@ -112,11 +157,13 @@ public abstract class MSLink { os.write(IconIndex); os.write(ShowCommand); os.write(Hotkey); - os.write(Reserved); + os.write(Reserved1); os.write(Reserved2); os.write(Reserved3); os.write(IDList); os.write(TerminalID); + + if (StringData.length > 0) os.write(StringData); } catch (IOException e) { e.printStackTrace(); diff --git a/app/src/main/java/com/winlator/core/OBBImageInstaller.java b/app/src/main/java/com/winlator/core/OBBImageInstaller.java deleted file mode 100644 index 50d996e..0000000 --- a/app/src/main/java/com/winlator/core/OBBImageInstaller.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.winlator.core; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.Uri; - -import com.winlator.MainActivity; -import com.winlator.R; -import com.winlator.SettingsFragment; -import com.winlator.contentdialog.ContentDialog; -import com.winlator.xenvironment.ImageFs; - -import java.io.File; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public abstract class OBBImageInstaller { - public static final byte LATEST_VERSION = 3; - private static final String DOWNLOAD_URL = "https://github.com/brunodev85/winlator/releases/download/v3.1.0/main."+LATEST_VERSION+".com.winlator.obb"; - - private static void installFromSource(final MainActivity activity, final Object source, int foundVersion, final Runnable onSuccessCallback) { - if (source instanceof Uri) { - Uri uri = (Uri)source; - Matcher matcher = Pattern.compile("main\\.([0-9]+)\\.com\\.winlator").matcher(uri.getPath()); - if (matcher.find()) { - foundVersion = Integer.parseInt(matcher.group(1)); - } - else { - AppUtils.showToast(activity, R.string.unable_to_install_obb_image); - return; - } - } - - ImageFs imageFs = ImageFs.find(activity); - final File rootDir = imageFs.getRootDir(); - - SettingsFragment.resetBox86_64Version(activity); - - if (foundVersion <= 0) foundVersion = imageFs.isValid() ? imageFs.getVersion() + 1 : 1; - final int finalVersion = foundVersion; - - activity.preloaderDialog.show(R.string.installing_obb_image); - Executors.newSingleThreadExecutor().execute(() -> { - clearRootDir(rootDir); - - boolean success = false; - if (source instanceof File) { - success = TarZstdUtils.extract((File)source, rootDir); - } - else if (source instanceof Uri) { - success = TarZstdUtils.extract(activity, (Uri)source, rootDir); - } - - if (success) { - imageFs.createImgVersionFile(finalVersion); - if (source instanceof File) ((File)source).delete(); - if (onSuccessCallback != null) activity.runOnUiThread(onSuccessCallback); - } - else AppUtils.showToast(activity, R.string.unable_to_install_obb_image); - - activity.preloaderDialog.closeOnUiThread(); - }); - } - - public static void openFileForInstall(final MainActivity activity, final Runnable onSuccessCallback) { - activity.setOpenFileCallback((uri) -> installFromSource(activity, uri, 0, onSuccessCallback)); - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - activity.startActivityForResult(intent, MainActivity.OPEN_FILE_REQUEST_CODE); - } - - public static void downloadFileForInstall(final MainActivity activity, final Runnable onSuccessCallback) { - AppUtils.keepScreenOn(activity); - String filename = DOWNLOAD_URL.substring(DOWNLOAD_URL.lastIndexOf("/") + 1); - final File destination = new File(activity.getCacheDir(), filename); - if (destination.isFile()) destination.delete(); - HttpUtils.download(activity, DOWNLOAD_URL, destination, (success) -> { - if (success) { - installFromSource(activity, destination, LATEST_VERSION, onSuccessCallback); - } - else AppUtils.showToast(activity, R.string.unable_to_download_file); - }); - } - - public static void installIfNeeded(final MainActivity activity) { - ImageFs imageFs = ImageFs.find(activity); - final AtomicReference obbFileRef = new AtomicReference<>(); - int foundVersion = findOBBFile(activity, obbFileRef); - - if (!imageFs.isValid() || imageFs.getVersion() < foundVersion) { - if (foundVersion == -1) { - ContentDialog.confirm(activity, R.string.unable_to_find_the_obb_image_do_you_want_to_download_file_and_install, () -> downloadFileForInstall(activity, null)); - } - else OBBImageInstaller.installFromSource(activity, obbFileRef.get(), foundVersion, null); - } - } - - private static void clearOptDir(File optDir) { - File[] files = optDir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.getName().equals("installed-wine")) continue; - FileUtils.delete(file); - } - } - } - - private static void clearRootDir(File rootDir) { - if (rootDir.isDirectory()) { - File[] files = rootDir.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) { - String name = file.getName(); - if (name.equals("home") || name.equals("opt")) { - if (name.equals("opt")) clearOptDir(file); - continue; - } - } - FileUtils.delete(file); - } - } - } - else rootDir.mkdirs(); - } - - private static int findOBBFile(Context context, AtomicReference result) { - String packageName = context.getPackageName(); - int foundObbVersion; - try { - foundObbVersion = context.getPackageManager().getPackageInfo(packageName, 0).versionCode; - } - catch (PackageManager.NameNotFoundException unused) { - foundObbVersion = 0; - } - - File obbDir = context.getObbDir(); - while (foundObbVersion >= 0) { - String filename = "main."+foundObbVersion+"."+packageName+".obb"; - File file = new File(obbDir, filename); - if (file.exists()) { - result.set(file); - return foundObbVersion; - } - foundObbVersion--; - } - foundObbVersion = -1; - return foundObbVersion; - } -} diff --git a/app/src/main/java/com/winlator/core/OnExtractFileListener.java b/app/src/main/java/com/winlator/core/OnExtractFileListener.java index 7b93ca5..242820f 100644 --- a/app/src/main/java/com/winlator/core/OnExtractFileListener.java +++ b/app/src/main/java/com/winlator/core/OnExtractFileListener.java @@ -3,5 +3,5 @@ package com.winlator.core; import java.io.File; public interface OnExtractFileListener { - File onExtractFile(File destination, String entryName); + File onExtractFile(File destination, long size); } diff --git a/app/src/main/java/com/winlator/core/ProcessHelper.java b/app/src/main/java/com/winlator/core/ProcessHelper.java index fb6474d..b2c505c 100644 --- a/app/src/main/java/com/winlator/core/ProcessHelper.java +++ b/app/src/main/java/com/winlator/core/ProcessHelper.java @@ -1,9 +1,12 @@ package com.winlator.core; +import android.os.Environment; import android.os.Process; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -14,6 +17,7 @@ import java.util.concurrent.Executors; public abstract class ProcessHelper { public static boolean debugMode = false; public static Callback debugCallback; + public static boolean generateDebugFile = false; private static final byte SIGCONT = 18; private static final byte SIGSTOP = 19; @@ -48,8 +52,8 @@ public abstract class ProcessHelper { Callback debugCallback = ProcessHelper.debugCallback; if (debugMode || debugCallback != null) { - createDebugThread(process.getInputStream(), debugCallback); - createDebugThread(process.getErrorStream(), debugCallback); + createDebugThread(false, process.getInputStream(), debugCallback); + createDebugThread(true, process.getErrorStream(), debugCallback); } if (terminationCallback != null) createWaitForThread(process, terminationCallback); @@ -58,15 +62,26 @@ public abstract class ProcessHelper { return pid; } - private static void createDebugThread(final InputStream inputStream, final Callback debugCallback) { + private static void createDebugThread(boolean isError, final InputStream inputStream, final Callback debugCallback) { Executors.newSingleThreadExecutor().execute(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; - while ((line = reader.readLine()) != null) { - if (debugCallback != null) { - debugCallback.call(line); + if (debugMode && generateDebugFile) { + File winlatorDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "Winlator"); + winlatorDir.mkdirs(); + final File debugFile = new File(winlatorDir, isError ? "debug-err.txt" : "debug-out.txt"); + if (debugFile.isFile()) debugFile.delete(); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(debugFile))) { + while ((line = reader.readLine()) != null) writer.write(line+"\n"); + } + } + else { + while ((line = reader.readLine()) != null) { + if (debugCallback != null) { + debugCallback.call(line); + } + else System.out.println(line); } - else System.out.println(line); } } catch (IOException e) {} diff --git a/app/src/main/java/com/winlator/core/StringUtils.java b/app/src/main/java/com/winlator/core/StringUtils.java index 92b24fa..0cb7758 100644 --- a/app/src/main/java/com/winlator/core/StringUtils.java +++ b/app/src/main/java/com/winlator/core/StringUtils.java @@ -31,6 +31,10 @@ public class StringUtils { return text.toString().toLowerCase(Locale.ENGLISH).replaceAll(" *\\(([^\\)]+)\\)$", "").replaceAll("( \\+ )+| +", "-"); } + public static String parseNumber(Object text) { + return text.toString().replaceAll("[^0-9\\.]+", ""); + } + public static String getString(Context context, String resName) { String string = null; try { diff --git a/app/src/main/java/com/winlator/core/TarZstdUtils.java b/app/src/main/java/com/winlator/core/TarCompressorUtils.java similarity index 51% rename from app/src/main/java/com/winlator/core/TarZstdUtils.java rename to app/src/main/java/com/winlator/core/TarCompressorUtils.java index 7fc2b6d..e22510d 100644 --- a/app/src/main/java/com/winlator/core/TarZstdUtils.java +++ b/app/src/main/java/com/winlator/core/TarCompressorUtils.java @@ -3,13 +3,14 @@ package com.winlator.core; import android.content.Context; import android.net.Uri; -import androidx.annotation.IntRange; - import org.apache.commons.compress.archivers.ArchiveInputStream; import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarConstants; +import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; +import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream; import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream; @@ -23,7 +24,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -public abstract class TarZstdUtils { +public abstract class TarCompressorUtils { + public enum Type {XZ, ZSTD} + private static void addFile(ArchiveOutputStream tar, File file, String entryName) { try { tar.putArchiveEntry(tar.createArchiveEntry(file, entryName)); @@ -35,12 +38,24 @@ public abstract class TarZstdUtils { catch (Exception e) {} } + private static void addLinkFile(ArchiveOutputStream tar, File file, String entryName) { + try { + TarArchiveEntry entry = new TarArchiveEntry(entryName, TarConstants.LF_SYMLINK); + entry.setLinkName(FileUtils.readSymlink(file)); + tar.putArchiveEntry(entry); + tar.closeArchiveEntry(); + } + catch (Exception e) {} + } + private static void addDirectory(ArchiveOutputStream tar, File folder, String basePath) throws IOException { File[] files = folder.listFiles(); if (files == null) return; for (File file : files) { - if (FileUtils.isSymlink(file)) continue; - if (file.isDirectory()) { + if (FileUtils.isSymlink(file)) { + addLinkFile(tar, file, basePath+file.getName()); + } + else if (file.isDirectory()) { String entryName = basePath+file.getName() + "/"; tar.putArchiveEntry(tar.createArchiveEntry(folder, entryName)); tar.closeArchiveEntry(); @@ -50,21 +65,23 @@ public abstract class TarZstdUtils { } } - public static void compress(File file, File zipFile) { - compress(file, zipFile, 3); + public static void compress(Type type, File file, File destination) { + compress(type, file, destination, 3); } - public static void compress(File file, File zipFile, @IntRange(from = 1, to = 19) int level) { - compress(new File[]{file}, zipFile, level); + public static void compress(Type type, File file, File destination, int level) { + compress(type, new File[]{file}, destination, level); } - public static void compress(File[] files, File destination, @IntRange(from = 1, to = 19) int level) { - try (OutputStream outStream = new ZstdCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(destination), StreamUtils.BUFFER_SIZE), level); + public static void compress(Type type, File[] files, File destination, int level) { + try (OutputStream outStream = getCompressorOutputStream(type, destination, level); TarArchiveOutputStream tar = new TarArchiveOutputStream(outStream)) { tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); for (File file : files) { - if (FileUtils.isSymlink(file)) continue; - if (file.isDirectory()) { + if (FileUtils.isSymlink(file)) { + addLinkFile(tar, file, file.getName()); + } + else if (file.isDirectory()) { String basePath = file.getName() + "/"; tar.putArchiveEntry(tar.createArchiveEntry(file, basePath)); tar.closeArchiveEntry(); @@ -77,45 +94,50 @@ public abstract class TarZstdUtils { catch (IOException e) {} } - public static boolean extract(Context context, String assetFile, File destination) { - return extract(context, assetFile, destination, null); + public static boolean extract(Type type, Context context, String assetFile, File destination) { + return extract(type, context, assetFile, destination, null); } - public static boolean extract(Context context, String assetFile, File destination, OnExtractFileListener onExtractFileListener) { + public static boolean extract(Type type, Context context, String assetFile, File destination, OnExtractFileListener onExtractFileListener) { try { - return extract(context.getAssets().open(assetFile), destination, onExtractFileListener); + return extract(type, context.getAssets().open(assetFile), destination, onExtractFileListener); } catch (IOException e) { return false; } } - public static boolean extract(File source, File destination) { - return extract(source, destination, null); + public static boolean extract(Type type, Context context, Uri source, File destination) { + return extract(type, context, source, destination, null); } - public static boolean extract(File source, File destination, OnExtractFileListener onExtractFileListener) { - if (source == null || !source.isFile()) return false; - try { - return extract(new BufferedInputStream(new FileInputStream(source), StreamUtils.BUFFER_SIZE), destination, onExtractFileListener); - } - catch (FileNotFoundException e) { - return false; - } - } - - public static boolean extract(Context context, Uri source, File destination) { + public static boolean extract(Type type, Context context, Uri source, File destination, OnExtractFileListener onExtractFileListener) { if (source == null) return false; try { - return extract(context.getContentResolver().openInputStream(source), destination, null); + return extract(type, context.getContentResolver().openInputStream(source), destination, onExtractFileListener); } catch (FileNotFoundException e) { return false; } } - private static boolean extract(InputStream source, File destination, OnExtractFileListener onExtractFileListener) { - try (InputStream inStream = new ZstdCompressorInputStream(source); + public static boolean extract(Type type, File source, File destination) { + return extract(type, source, destination, null); + } + + public static boolean extract(Type type, File source, File destination, OnExtractFileListener onExtractFileListener) { + if (source == null || !source.isFile()) return false; + try { + return extract(type, new BufferedInputStream(new FileInputStream(source), StreamUtils.BUFFER_SIZE), destination, onExtractFileListener); + } + catch (FileNotFoundException e) { + return false; + } + } + + private static boolean extract(Type type, InputStream source, File destination, OnExtractFileListener onExtractFileListener) { + if (source == null) return false; + try (InputStream inStream = getCompressorInputStream(type, source); ArchiveInputStream tar = new TarArchiveInputStream(inStream)) { TarArchiveEntry entry; while ((entry = (TarArchiveEntry)tar.getNextEntry()) != null) { @@ -123,7 +145,7 @@ public abstract class TarZstdUtils { File file = new File(destination, entry.getName()); if (onExtractFileListener != null) { - file = onExtractFileListener.onExtractFile(destination, entry.getName()); + file = onExtractFileListener.onExtractFile(file, entry.getSize()); if (file == null) continue; } @@ -149,4 +171,24 @@ public abstract class TarZstdUtils { return false; } } + + private static InputStream getCompressorInputStream(Type type, InputStream source) throws IOException { + if (type == Type.XZ) { + return new XZCompressorInputStream(source); + } + else if (type == Type.ZSTD) { + return new ZstdCompressorInputStream(source); + } + return null; + } + + private static OutputStream getCompressorOutputStream(Type type, File destination, int level) throws IOException { + if (type == Type.XZ) { + return new XZCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(destination), StreamUtils.BUFFER_SIZE), level); + } + else if (type == Type.ZSTD) { + return new ZstdCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(destination), StreamUtils.BUFFER_SIZE), level); + } + return null; + } } diff --git a/app/src/main/java/com/winlator/core/TarXZUtils.java b/app/src/main/java/com/winlator/core/TarXZUtils.java deleted file mode 100644 index 75a8e8e..0000000 --- a/app/src/main/java/com/winlator/core/TarXZUtils.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.winlator.core; - -import android.content.Context; -import android.net.Uri; - -import org.apache.commons.compress.archivers.ArchiveInputStream; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -public abstract class TarXZUtils { - public static boolean extract(Context context, String assetFile, File destination) { - try { - return extract(context.getAssets().open(assetFile), destination); - } - catch (IOException e) { - return false; - } - } - - public static boolean extract(File source, File destination) { - if (source == null || !source.isFile()) return false; - try { - return extract(new BufferedInputStream(new FileInputStream(source), StreamUtils.BUFFER_SIZE), destination); - } - catch (FileNotFoundException e) { - return false; - } - } - - public static boolean extract(Context context, Uri source, File destination) { - if (source == null) return false; - try { - return extract(context.getContentResolver().openInputStream(source), destination); - } - catch (FileNotFoundException e) { - return false; - } - } - - private static boolean extract(InputStream source, File destination) { - if (source == null) return false; - try (InputStream inStream = new XZCompressorInputStream(source); - ArchiveInputStream tar = new TarArchiveInputStream(inStream)) { - TarArchiveEntry entry; - while ((entry = (TarArchiveEntry)tar.getNextEntry()) != null) { - if (!tar.canReadEntryData(entry)) continue; - File file = new File(destination, entry.getName()); - - if (entry.isDirectory()) { - if (!file.isDirectory()) file.mkdirs(); - } - else { - if (entry.isSymbolicLink()) { - FileUtils.symlink(entry.getLinkName(), file.getAbsolutePath()); - } - else { - try (BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(file), StreamUtils.BUFFER_SIZE)) { - if (!StreamUtils.copy(tar, outStream)) return false; - } - } - } - - FileUtils.chmod(file, 0771); - } - return true; - } - catch (IOException e) { - return false; - } - } -} diff --git a/app/src/main/java/com/winlator/core/WineInfo.java b/app/src/main/java/com/winlator/core/WineInfo.java index d8517bd..b767bac 100644 --- a/app/src/main/java/com/winlator/core/WineInfo.java +++ b/app/src/main/java/com/winlator/core/WineInfo.java @@ -13,7 +13,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class WineInfo implements Parcelable { - public static final WineInfo MAIN_WINE_VERSION = new WineInfo("8.0.1", "x86_64"); + public static final WineInfo MAIN_WINE_VERSION = new WineInfo("8.0.2", "x86_64"); private static final Pattern pattern = Pattern.compile("^wine\\-([0-9\\.]+)\\-?([0-9\\.]+)?\\-(x86|x86_64)$"); public final String version; public final String subversion; @@ -53,6 +53,10 @@ public class WineInfo implements Parcelable { return arch.equals("x86_64"); } + public String binName() { + return isWin64() ? "wine64" : "wine"; + } + public String identifier() { return "wine-"+fullVersion()+"-"+arch; } diff --git a/app/src/main/java/com/winlator/core/WineRegistryEditor.java b/app/src/main/java/com/winlator/core/WineRegistryEditor.java index 02e7ab4..27efde1 100644 --- a/app/src/main/java/com/winlator/core/WineRegistryEditor.java +++ b/app/src/main/java/com/winlator/core/WineRegistryEditor.java @@ -276,7 +276,6 @@ public class WineRegistryEditor implements Closeable { try (BufferedReader reader = new BufferedReader(new FileReader(cloneFile), StreamUtils.BUFFER_SIZE)) { key = "["+escape(key)+(!keyAsPrefix ? "]" : ""); int totalLength = 0; - int index; int start = -1; int end = -1; int emptyLines = 0; @@ -285,15 +284,13 @@ public class WineRegistryEditor implements Closeable { String line; while ((line = reader.readLine()) != null) { if (start == -1) { - index = line.indexOf(key); - if (index != -1) { - offset = totalLength + index - 1; + if (line.startsWith(key)) { + offset = totalLength - 1; start = totalLength + line.length() + 1; } } else { - index = line.indexOf('['); - if (index != -1) { + if (line.startsWith("[")) { end = Math.max(-1, totalLength - emptyLines - 1); break; } @@ -330,7 +327,6 @@ public class WineRegistryEditor implements Closeable { reader.skip(keyLocation.start); name = name != null ? "\""+escape(name)+"\"=" : "@="; int totalLength = 0; - int index; int start = -1; int end = -1; int offset = 0; @@ -338,10 +334,9 @@ public class WineRegistryEditor implements Closeable { String line; while ((line = reader.readLine()) != null && totalLength < keyLocation.length()) { if (start == -1) { - index = line.indexOf(name); - if (index != -1) { - offset = totalLength + index - 1; - start = totalLength + name.length() + index; + if (line.startsWith(name)) { + offset = totalLength - 1; + start = totalLength + name.length(); } } else { diff --git a/app/src/main/java/com/winlator/core/WineStartMenuCreator.java b/app/src/main/java/com/winlator/core/WineStartMenuCreator.java index 9fd6ac4..7f52dae 100644 --- a/app/src/main/java/com/winlator/core/WineStartMenuCreator.java +++ b/app/src/main/java/com/winlator/core/WineStartMenuCreator.java @@ -11,6 +11,16 @@ import org.json.JSONObject; import java.io.File; public abstract class WineStartMenuCreator { + private static int parseShowCommand(String value) { + if (value.equals("SW_SHOWMAXIMIZED")) { + return MSLink.SW_SHOWMAXIMIZED; + } + else if (value.equals("SW_SHOWMINNOACTIVE")) { + return MSLink.SW_SHOWMINNOACTIVE; + } + else return MSLink.SW_SHOWNORMAL; + } + private static void createMenuEntry(JSONObject item, File currentDir) throws JSONException { if (item.has("children")) { currentDir = new File(currentDir, item.getString("name")); @@ -21,7 +31,13 @@ public abstract class WineStartMenuCreator { } else { File outputFile = new File(currentDir, item.getString("name")+".lnk"); - MSLink.createFile(item.getString("path"), outputFile); + MSLink.Options options = new MSLink.Options(); + options.targetPath = item.getString("path"); + options.cmdArgs = item.optString("cmdArgs"); + options.iconLocation = item.optString("iconLocation", options.targetPath); + options.iconIndex = item.optInt("iconIndex", 0); + if (item.has("showCommand")) options.showCommand = parseShowCommand(item.getString("showCommand")); + MSLink.createFile(options, outputFile); } } diff --git a/app/src/main/java/com/winlator/core/WineThemeManager.java b/app/src/main/java/com/winlator/core/WineThemeManager.java index 993a634..c04c7c0 100644 --- a/app/src/main/java/com/winlator/core/WineThemeManager.java +++ b/app/src/main/java/com/winlator/core/WineThemeManager.java @@ -1,28 +1,59 @@ package com.winlator.core; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.DisplayMetrics; +import com.winlator.R; import com.winlator.xenvironment.ImageFs; +import com.winlator.xserver.ScreenInfo; import java.io.File; public abstract class WineThemeManager { public enum Theme {LIGHT, DARK} - public static final Theme DEFAULT_THEME = Theme.LIGHT; - public static final String DEFAULT_BACKGROUND = "#0277bd"; + public enum BackgroundType {IMAGE, COLOR} + public static final String DEFAULT_DESKTOP_THEME = Theme.LIGHT+","+BackgroundType.IMAGE+",#0277bd"; - public static void apply(Context context, Theme theme, String backgroundColor) { - apply(context, theme, Color.parseColor(backgroundColor)); + public static class ThemeInfo { + public final Theme theme; + public final BackgroundType backgroundType; + public final int backgroundColor; + + public ThemeInfo(String value) { + String[] values = value.split(","); + theme = Theme.valueOf(values[0]); + if (values.length < 3) { + backgroundColor = Color.parseColor(values[1]); + backgroundType = BackgroundType.IMAGE; + } + else { + backgroundType = BackgroundType.valueOf(values[1]); + backgroundColor = Color.parseColor(values[2]); + } + } } - public static void apply(Context context, Theme theme, int backgroundColor) { + public static void apply(Context context, ThemeInfo themeInfo, ScreenInfo screenInfo) { File rootDir = ImageFs.find(context).getRootDir(); File userRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/user.reg"); - String background = Color.red(backgroundColor)+" "+Color.green(backgroundColor)+" "+Color.blue(backgroundColor); + String background = Color.red(themeInfo.backgroundColor)+" "+Color.green(themeInfo.backgroundColor)+" "+Color.blue(themeInfo.backgroundColor); + + if (themeInfo.backgroundType == BackgroundType.IMAGE) createWallpaperBMPFile(context, screenInfo); try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) { - if (theme == Theme.LIGHT) { + if (themeInfo.backgroundType == BackgroundType.IMAGE) { + registryEditor.setStringValue("Control Panel\\Desktop", "Wallpaper", ImageFs.CACHE_PATH+"/wallpaper.bmp"); + } + else registryEditor.removeValue("Control Panel\\Desktop", "Wallpaper"); + + if (themeInfo.theme == Theme.LIGHT) { registryEditor.setStringValue("Control Panel\\Colors", "ActiveBorder", "245 245 245"); registryEditor.setStringValue("Control Panel\\Colors", "ActiveTitle", "96 125 139"); registryEditor.setStringValue("Control Panel\\Colors", "Background", background); @@ -54,7 +85,7 @@ public abstract class WineThemeManager { registryEditor.setStringValue("Control Panel\\Colors", "WindowFrame", "158 158 158"); registryEditor.setStringValue("Control Panel\\Colors", "WindowText", "0 0 0"); } - else if (theme == Theme.DARK) { + else if (themeInfo.theme == Theme.DARK) { registryEditor.setStringValue("Control Panel\\Colors", "ActiveBorder", "48 48 48"); registryEditor.setStringValue("Control Panel\\Colors", "ActiveTitle", "33 33 33"); registryEditor.setStringValue("Control Panel\\Colors", "Background", background); @@ -88,4 +119,45 @@ public abstract class WineThemeManager { } } } + + private static void createWallpaperBMPFile(Context context, ScreenInfo screenInfo) { + final int outputHeight = 480; + int outputWidth = (int)Math.ceil(((float)outputHeight / screenInfo.height) * screenInfo.width); + + Bitmap outputBitmap = Bitmap.createBitmap(outputWidth, outputHeight, Bitmap.Config.ARGB_8888); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Canvas canvas = new Canvas(outputBitmap); + + File userWallpaperFile = getUserWallpaperFile(context); + if (userWallpaperFile.isFile()) { + Bitmap image = BitmapFactory.decodeFile(userWallpaperFile.getPath()); + Rect srcRect = new Rect(0, 0, image.getWidth(), image.getHeight()); + Rect dstRect = new Rect(0, 0, outputWidth, outputHeight); + canvas.drawBitmap(image, srcRect, dstRect, paint); + } + else { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inTargetDensity = DisplayMetrics.DENSITY_HIGH; + Bitmap wallpaperBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.wallpaper, options); + paint.setStyle(Paint.Style.FILL); + paint.setColor(0xff01579b); + canvas.drawRect(0, 0, outputWidth, outputHeight * 0.5f, paint); + paint.setColor(0xff0277bd); + canvas.drawRect(0, outputHeight * 0.5f, outputWidth, outputHeight, paint); + + float targetSize = outputHeight * (320.0f / 480.0f); + float centerX = (outputWidth - targetSize) * 0.5f; + float centerY = (outputHeight - targetSize) * 0.5f; + Rect srcRect = new Rect(0, 0, wallpaperBitmap.getWidth(), wallpaperBitmap.getHeight()); + RectF dstRect = new RectF(centerX, centerY, centerX + targetSize, centerY + targetSize); + canvas.drawBitmap(wallpaperBitmap, srcRect, dstRect, paint); + } + + ImageFs imageFs = ImageFs.find(context); + MSBitmap.create(outputBitmap, new File(imageFs.getRootDir(), ImageFs.CACHE_PATH+"/wallpaper.bmp")); + } + + public static File getUserWallpaperFile(Context context) { + return new File(ImageFs.find(context).getRootDir(), ImageFs.CONFIG_PATH+"/user-wallpaper.png"); + } } diff --git a/app/src/main/java/com/winlator/core/WineUtils.java b/app/src/main/java/com/winlator/core/WineUtils.java index 2c0e98d..2386e3d 100644 --- a/app/src/main/java/com/winlator/core/WineUtils.java +++ b/app/src/main/java/com/winlator/core/WineUtils.java @@ -40,7 +40,7 @@ public abstract class WineUtils { File destination = new File(ImageFs.find(context).getInstalledWineDir(), "/preinstall/wine"); FileUtils.delete(destination); destination.mkdirs(); - boolean success = TarXZUtils.extract(context, uri, destination); + boolean success = TarCompressorUtils.extract(TarCompressorUtils.Type.XZ, context, uri, destination); if (!success) FileUtils.delete(destination); if (callback != null) callback.call(success ? destination : null); }); @@ -143,48 +143,75 @@ public abstract class WineUtils { return wineInfos; } - public static void applyRegistryTweaks(Context context) { + public static void applySystemTweaks(Context context, WineInfo wineInfo, boolean firstTimeBoot) { File rootDir = ImageFs.find(context).getRootDir(); File systemRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/system.reg"); File userRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/user.reg"); try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) { - registryEditor.setStringValue("Software\\Classes\\.reg", null, "REGfile"); - registryEditor.setStringValue("Software\\Classes\\.reg", "Content Type", "application/reg"); - registryEditor.setStringValue("Software\\Classes\\REGfile\\Shell\\Open\\command", null, "C:\\windows\\regedit.exe /C \"%1\""); + if (firstTimeBoot) { + registryEditor.setStringValue("Software\\Classes\\.reg", null, "REGfile"); + registryEditor.setStringValue("Software\\Classes\\.reg", "Content Type", "application/reg"); + registryEditor.setStringValue("Software\\Classes\\REGfile\\Shell\\Open\\command", null, "C:\\windows\\regedit.exe /C \"%1\""); - registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableHidraw", 1); - registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableInput", 1); - registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "Enable SDL", 0); + registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableHidraw", 1); + registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableInput", 1); + registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "Enable SDL", 0); + } + + registryEditor.setStringValue("Software\\Classes\\dllfile\\DefaultIcon", null, "shell32.dll,-154"); + registryEditor.setStringValue("Software\\Classes\\lnkfile\\DefaultIcon", null, "shell32.dll,-30"); + registryEditor.setStringValue("Software\\Classes\\inifile\\DefaultIcon", null, "shell32.dll,-151"); } - final String[] dllOverrides = {"d3d8", "d3d9", "d3d10", "d3d10_1", "d3d10core", "d3d11", "d3d12", "d3d12core", "ddraw", "dxgi", "wined3d", "dinput", "dinput8", "xinput1_1", "xinput1_2", "xinput1_3", "xinput1_4", "xinput9_1_0", "xinputuap"}; + final String[] direct3dLibs = {"d3d8", "d3d9", "d3d10", "d3d10_1", "d3d10core", "d3d11", "d3d12", "d3d12core", "ddraw", "dxgi", "wined3d"}; + final String[] xinputLibs = {"dinput", "dinput8", "xinput1_1", "xinput1_2", "xinput1_3", "xinput1_4", "xinput9_1_0", "xinputuap"}; final String dllOverridesKey = "Software\\Wine\\DllOverrides"; try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) { - for (String name : dllOverrides) registryEditor.setStringValue(dllOverridesKey, name, "native,builtin"); + for (String name : direct3dLibs) registryEditor.setStringValue(dllOverridesKey, name, "native,builtin"); + + boolean isMainWineVersion = wineInfo.identifier().equals(WineInfo.MAIN_WINE_VERSION.identifier()); + for (String name : xinputLibs) registryEditor.setStringValue(dllOverridesKey, name, isMainWineVersion ? "builtin,native" : "native,builtin"); + + registryEditor.removeKey("Software\\Winlator\\WFM\\ContextMenu\\7-Zip"); + registryEditor.setStringValue("Software\\Winlator\\WFM\\ContextMenu\\7-Zip", "Open Archive", "Z:\\opt\\apps\\7-Zip\\7zFM.exe \"%FILE%\""); + registryEditor.setStringValue("Software\\Winlator\\WFM\\ContextMenu\\7-Zip", "Extract Here", "Z:\\opt\\apps\\7-Zip\\7zG.exe x \"%FILE%\" -r -o\"%DIR%\" -y"); + registryEditor.setStringValue("Software\\Winlator\\WFM\\ContextMenu\\7-Zip", "Extract to Folder", "Z:\\opt\\apps\\7-Zip\\7zG.exe x \"%FILE%\" -r -o\"%DIR%\\%BASENAME%\" -y"); + } + + File wineSystem32Dir = new File(rootDir, "/opt/wine/lib/wine/x86_64-windows"); + File wineSysWoW64Dir = new File(rootDir, "/opt/wine/lib/wine/i386-windows"); + File containerSystem32Dir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/windows/system32"); + File containerSysWoW64Dir = new File(rootDir, ImageFs.WINEPREFIX+"/drive_c/windows/syswow64"); + + final String[] dlnames = {"user32.dll", "shell32.dll", "dinput.dll", "dinput8.dll", "xinput1_1.dll", "xinput1_2.dll", "xinput1_3.dll", "xinput1_4.dll", "xinput9_1_0.dll", "xinputuap.dll", "winemenubuilder.exe", "explorer.exe"}; + boolean win64 = wineInfo.isWin64(); + for (String dlname : dlnames) { + FileUtils.copy(new File(wineSysWoW64Dir, dlname), new File(win64 ? containerSysWoW64Dir : containerSystem32Dir, dlname)); + if (win64) FileUtils.copy(new File(wineSystem32Dir, dlname), new File(containerSystem32Dir, dlname)); } } - public static void overrideDXComponentDlls(Context context, Container container, String dxcomponents) { + public static void overrideWinComponentDlls(Context context, Container container, String wincomponents) { final String dllOverridesKey = "Software\\Wine\\DllOverrides"; File userRegFile = new File(container.getRootDir(), ".wine/user.reg"); - Iterator oldDXComponentsIter = Container.dxcomponentsIterator(container.getExtra("dxcomponents", dxcomponents)).iterator(); + Iterator oldWinComponentsIter = new KeyValueSet(container.getExtra("wincomponents", Container.FALLBACK_WINCOMPONENTS)).iterator(); try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) { - JSONObject dxcomponentsJSONObject = new JSONObject(FileUtils.readString(context, "dxcomponents.json")); + JSONObject wincomponentsJSONObject = new JSONObject(FileUtils.readString(context, "wincomponents/wincomponents.json")); - for (String[] dxcomponent : Container.dxcomponentsIterator(dxcomponents)) { - if (dxcomponent[1].equals(oldDXComponentsIter.next()[1])) continue; - boolean useNative = dxcomponent[1].equals("1"); + for (String[] wincomponent : new KeyValueSet(wincomponents)) { + if (wincomponent[1].equals(oldWinComponentsIter.next()[1])) continue; + boolean useNative = wincomponent[1].equals("1"); - JSONArray libNames = dxcomponentsJSONObject.getJSONArray(dxcomponent[0]); - for (int i = 0; i < libNames.length(); i++) { - String libName = libNames.getString(i); + JSONArray dlnames = wincomponentsJSONObject.getJSONArray(wincomponent[0]); + for (int i = 0; i < dlnames.length(); i++) { + String dlname = dlnames.getString(i); if (useNative) { - registryEditor.setStringValue(dllOverridesKey, libName, "native,builtin"); + registryEditor.setStringValue(dllOverridesKey, dlname, "native,builtin"); } - else registryEditor.removeValue(dllOverridesKey, libName); + else registryEditor.removeValue(dllOverridesKey, dlname); } } } diff --git a/app/src/main/java/com/winlator/core/Workarounds.java b/app/src/main/java/com/winlator/core/Workarounds.java new file mode 100644 index 0000000..8141aec --- /dev/null +++ b/app/src/main/java/com/winlator/core/Workarounds.java @@ -0,0 +1,22 @@ +package com.winlator.core; + +import com.winlator.winhandler.WinHandler; +import com.winlator.xserver.Window; + +import java.util.Timer; +import java.util.TimerTask; + +public abstract class Workarounds { + // Workaround for applications that don't work mouse/keyboard + public static void onMapWindow(final WinHandler winHandler, Window window) { + final String className = window.getClassName(); + if (className.equals("twfc.exe")) { + (new Timer()).schedule(new TimerTask() { + @Override + public void run() { + winHandler.bringToFront(className, true); + } + }, 500); + } + } +} diff --git a/app/src/main/java/com/winlator/inputcontrols/Binding.java b/app/src/main/java/com/winlator/inputcontrols/Binding.java index 3e26fb3..4598aab 100644 --- a/app/src/main/java/com/winlator/inputcontrols/Binding.java +++ b/app/src/main/java/com/winlator/inputcontrols/Binding.java @@ -8,7 +8,7 @@ import com.winlator.xserver.XKeycode; import java.util.ArrayList; public enum Binding { - NONE, MOUSE_LEFT_BUTTON, MOUSE_MIDDLE_BUTTON, MOUSE_RIGHT_BUTTON, MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, MOUSE_SCROLL_UP, MOUSE_SCROLL_DOWN, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT, KEY_ENTER, KEY_ESC, KEY_BKSP, KEY_DEL, KEY_TAB, KEY_SPACE, KEY_CTRL_L, KEY_CTRL_R, KEY_SHIFT_L, KEY_SHIFT_R, KEY_ALT_L, KEY_ALT_R, KEY_HOME, KEY_PRTSCN, KEY_CAPS_LOCK, KEY_NUM_LOCK, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_BRACKET_LEFT, KEY_BRACKET_RIGHT, KEY_BACKSLASH, KEY_SLASH, KEY_SEMICOLON, KEY_COMMA, KEY_PERIOD, KEY_APOSTROPHE, KEY_KP_ADD, KEY_MINUS, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9, GAMEPAD_BUTTON_A, GAMEPAD_BUTTON_B, GAMEPAD_BUTTON_X, GAMEPAD_BUTTON_Y, GAMEPAD_BUTTON_L1, GAMEPAD_BUTTON_R1, GAMEPAD_BUTTON_SELECT, GAMEPAD_BUTTON_START, GAMEPAD_BUTTON_L3, GAMEPAD_BUTTON_R3, GAMEPAD_BUTTON_L2, GAMEPAD_BUTTON_R2, GAMEPAD_LEFT_THUMB_UP, GAMEPAD_LEFT_THUMB_RIGHT, GAMEPAD_LEFT_THUMB_DOWN, GAMEPAD_LEFT_THUMB_LEFT, GAMEPAD_RIGHT_THUMB_UP, GAMEPAD_RIGHT_THUMB_RIGHT, GAMEPAD_RIGHT_THUMB_DOWN, GAMEPAD_RIGHT_THUMB_LEFT, GAMEPAD_DPAD_UP, GAMEPAD_DPAD_RIGHT, GAMEPAD_DPAD_DOWN, GAMEPAD_DPAD_LEFT; + NONE, MOUSE_LEFT_BUTTON, MOUSE_MIDDLE_BUTTON, MOUSE_RIGHT_BUTTON, MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, MOUSE_SCROLL_UP, MOUSE_SCROLL_DOWN, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT, KEY_ENTER, KEY_ESC, KEY_BKSP, KEY_DEL, KEY_TAB, KEY_SPACE, KEY_CTRL_L, KEY_CTRL_R, KEY_SHIFT_L, KEY_SHIFT_R, KEY_ALT_L, KEY_ALT_R, KEY_HOME, KEY_PRTSCN, KEY_PG_UP, KEY_PG_DOWN, KEY_CAPS_LOCK, KEY_NUM_LOCK, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_BRACKET_LEFT, KEY_BRACKET_RIGHT, KEY_BACKSLASH, KEY_SLASH, KEY_SEMICOLON, KEY_COMMA, KEY_PERIOD, KEY_APOSTROPHE, KEY_KP_ADD, KEY_MINUS, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9, GAMEPAD_BUTTON_A, GAMEPAD_BUTTON_B, GAMEPAD_BUTTON_X, GAMEPAD_BUTTON_Y, GAMEPAD_BUTTON_L1, GAMEPAD_BUTTON_R1, GAMEPAD_BUTTON_SELECT, GAMEPAD_BUTTON_START, GAMEPAD_BUTTON_L3, GAMEPAD_BUTTON_R3, GAMEPAD_BUTTON_L2, GAMEPAD_BUTTON_R2, GAMEPAD_LEFT_THUMB_UP, GAMEPAD_LEFT_THUMB_RIGHT, GAMEPAD_LEFT_THUMB_DOWN, GAMEPAD_LEFT_THUMB_LEFT, GAMEPAD_RIGHT_THUMB_UP, GAMEPAD_RIGHT_THUMB_RIGHT, GAMEPAD_RIGHT_THUMB_DOWN, GAMEPAD_RIGHT_THUMB_LEFT, GAMEPAD_DPAD_UP, GAMEPAD_DPAD_RIGHT, GAMEPAD_DPAD_DOWN, GAMEPAD_DPAD_LEFT; public final XKeycode keycode; Binding() { @@ -18,6 +18,13 @@ public enum Binding { } catch (IllegalArgumentException e) { keycode = XKeycode.KEY_NONE; + String name = name(); + if (name.equals("KEY_PG_UP")) { + keycode = XKeycode.KEY_PRIOR; + } + else if (name.equals("KEY_PG_DOWN")) { + keycode = XKeycode.KEY_NEXT; + } } this.keycode = keycode; } diff --git a/app/src/main/java/com/winlator/inputcontrols/ControlElement.java b/app/src/main/java/com/winlator/inputcontrols/ControlElement.java index 67383cc..630594b 100644 --- a/app/src/main/java/com/winlator/inputcontrols/ControlElement.java +++ b/app/src/main/java/com/winlator/inputcontrols/ControlElement.java @@ -43,7 +43,7 @@ public class ControlElement { } } public enum Range { - FROM_A_TO_Z(26), FROM_0_TO_9(10), FROM_F1_TO_F12(12); + FROM_A_TO_Z(26), FROM_0_TO_9(10), FROM_F1_TO_F12(12), FROM_NP0_TO_NP9(10); public final byte max; Range(int max) { @@ -310,6 +310,9 @@ public class ControlElement { case FROM_F1_TO_F12: text = "F"+(index + 1); break; + case FROM_NP0_TO_NP9: + text = "NP"+((index + 1) % 10); + break; } return text; } @@ -561,6 +564,9 @@ public class ControlElement { case FROM_F1_TO_F12: binding = Binding.valueOf("KEY_F"+(index + 1)); break; + case FROM_NP0_TO_NP9: + binding = Binding.valueOf("KEY_KP_"+((index + 1) % 10)); + break; } Arrays.fill(bindings, Binding.NONE); diff --git a/app/src/main/java/com/winlator/inputcontrols/ControlsProfile.java b/app/src/main/java/com/winlator/inputcontrols/ControlsProfile.java index 18a8950..2bad4b8 100644 --- a/app/src/main/java/com/winlator/inputcontrols/ControlsProfile.java +++ b/app/src/main/java/com/winlator/inputcontrols/ControlsProfile.java @@ -107,7 +107,6 @@ public class ControlsProfile implements Comparable { data.put("id", id); data.put("name", name); data.put("cursorSpeed", Float.valueOf(cursorSpeed)); - if (virtualGamepad) data.put("virtualGamepad", true); JSONArray elementsJSONArray = new JSONArray(); if (!elementsLoaded && file.isFile()) { diff --git a/app/src/main/java/com/winlator/inputcontrols/GamepadState.java b/app/src/main/java/com/winlator/inputcontrols/GamepadState.java index 057bc85..121d13f 100644 --- a/app/src/main/java/com/winlator/inputcontrols/GamepadState.java +++ b/app/src/main/java/com/winlator/inputcontrols/GamepadState.java @@ -2,6 +2,7 @@ package com.winlator.inputcontrols; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.Arrays; public class GamepadState { public float thumbLX = 0; @@ -58,4 +59,13 @@ public class GamepadState { public byte getDPadY() { return (byte)(dpad[0] ? -1 : (dpad[2] ? 1 : 0)); } + + public void copy(GamepadState other) { + this.thumbLX = other.thumbLX; + this.thumbLY = other.thumbLY; + this.thumbRX = other.thumbRX; + this.thumbRY = other.thumbRY; + this.buttons = other.buttons; + System.arraycopy(other.dpad, 0, this.dpad, 0, 4); + } } diff --git a/app/src/main/java/com/winlator/math/XForm.java b/app/src/main/java/com/winlator/math/XForm.java index e6f7f6e..5d2e74b 100644 --- a/app/src/main/java/com/winlator/math/XForm.java +++ b/app/src/main/java/com/winlator/math/XForm.java @@ -93,4 +93,14 @@ public class XForm { result[5] = a4 * b1 + a5 * b3 + b5; return result; } + + public static float[] transformPoint(float[] xform, float x, float y) { + return transformPoint(xform, x, y, new float[2]); + } + + public static float[] transformPoint(float[] xform, float x, float y, float[] result) { + result[0] = xform[0] * x + xform[2] * y + xform[4]; + result[1] = xform[1] * x + xform[3] * y + xform[5]; + return result; + } } diff --git a/app/src/main/java/com/winlator/renderer/GLRenderer.java b/app/src/main/java/com/winlator/renderer/GLRenderer.java index 4705300..378688d 100644 --- a/app/src/main/java/com/winlator/renderer/GLRenderer.java +++ b/app/src/main/java/com/winlator/renderer/GLRenderer.java @@ -36,15 +36,19 @@ public class GLRenderer implements GLSurfaceView.Renderer, WindowManager.OnWindo private final float[] tmpXForm2 = XForm.getInstance(); private final CursorMaterial cursorMaterial = new CursorMaterial(); private final WindowMaterial windowMaterial = new WindowMaterial(); - private final TransformationInfo transformationInfo = new TransformationInfo(); + public final Viewport viewport = new Viewport(); private final Drawable rootCursorDrawable; private final ArrayList renderableWindows = new ArrayList<>(); private String forceFullscreenWMClass = null; private boolean fullscreen = false; private boolean toggleFullscreen = false; + private boolean viewportNeedsUpdate = true; private boolean cursorVisible = true; private boolean screenOffsetYRelativeToCursor = false; private String[] unviewableWMClasses = null; + private float magnifierZoom = 1.0f; + private int surfaceWidth; + private int surfaceHeight; public GLRenderer(XServerView xServerView, XServer xServer) { this.xServerView = xServerView; @@ -79,8 +83,9 @@ public class GLRenderer implements GLSurfaceView.Renderer, WindowManager.OnWindo @Override public void onSurfaceChanged(GL10 gl, int width, int height) { - transformationInfo.update(width, height, xServer.screenInfo.width, xServer.screenInfo.height); - GLES20.glViewport(0, 0, width, height); + surfaceWidth = width; + surfaceHeight = height; + viewport.update(width, height, xServer.screenInfo.width, xServer.screenInfo.height); } @Override @@ -88,33 +93,41 @@ public class GLRenderer implements GLSurfaceView.Renderer, WindowManager.OnWindo if (toggleFullscreen) { fullscreen = !fullscreen; toggleFullscreen = false; + viewportNeedsUpdate = true; } drawFrame(); } private void drawFrame() { + if (viewportNeedsUpdate) { + if (fullscreen) { + GLES20.glViewport(0, 0, surfaceWidth, surfaceHeight); + } + else GLES20.glViewport(viewport.x, viewport.y, viewport.width, viewport.height); + viewportNeedsUpdate = false; + } + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); - if (!fullscreen) { - int pointerY = 0; - if (screenOffsetYRelativeToCursor) { - short halfScreenHeight = (short)(xServer.screenInfo.height / 2); - pointerY = Mathf.clamp(xServer.pointer.getY() - halfScreenHeight / 2, 0, halfScreenHeight); - } - XForm.makeTransform(tmpXForm2, transformationInfo.sceneOffsetX, transformationInfo.sceneOffsetY - pointerY, transformationInfo.sceneScaleX, transformationInfo.sceneScaleY, 0); - } - else XForm.identity(tmpXForm2); + float pointerX = 0; + float pointerY = 0; + float magnifierZoom = !screenOffsetYRelativeToCursor ? this.magnifierZoom : 1.0f; - if (!fullscreen) { - GLES20.glEnable(GLES20.GL_SCISSOR_TEST); - GLES20.glScissor(transformationInfo.viewOffsetX, transformationInfo.viewOffsetY, transformationInfo.viewWidth, transformationInfo.viewHeight); + if (magnifierZoom != 1.0f) { + pointerX = Mathf.clamp(xServer.pointer.getX() * magnifierZoom - xServer.screenInfo.width * 0.5f, 0, xServer.screenInfo.width * Math.abs(1.0f - magnifierZoom)); } + if (screenOffsetYRelativeToCursor || magnifierZoom != 1.0f) { + float scaleY = magnifierZoom != 1.0f ? Math.abs(1.0f - magnifierZoom) : 0.5f; + float offsetY = xServer.screenInfo.height * (screenOffsetYRelativeToCursor ? 0.25f : 0.5f); + pointerY = Mathf.clamp(xServer.pointer.getY() * magnifierZoom - offsetY, 0, xServer.screenInfo.height * scaleY); + } + + XForm.makeTransform(tmpXForm2, -pointerX, -pointerY, magnifierZoom, magnifierZoom, 0); + renderWindows(); if (cursorVisible) renderCursor(); - - if (!fullscreen) GLES20.glDisable(GLES20.GL_SCISSOR_TEST); } @Override @@ -342,4 +355,17 @@ public class GLRenderer implements GLSurfaceView.Renderer, WindowManager.OnWindo public void setUnviewableWMClasses(String... unviewableWMNames) { this.unviewableWMClasses = unviewableWMNames; } + + public boolean isFullscreen() { + return fullscreen; + } + + public float getMagnifierZoom() { + return magnifierZoom; + } + + public void setMagnifierZoom(float magnifierZoom) { + this.magnifierZoom = magnifierZoom; + xServerView.requestRender(); + } } diff --git a/app/src/main/java/com/winlator/renderer/TransformationInfo.java b/app/src/main/java/com/winlator/renderer/TransformationInfo.java deleted file mode 100644 index 258f59d..0000000 --- a/app/src/main/java/com/winlator/renderer/TransformationInfo.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.winlator.renderer; - -class TransformationInfo { - int viewWidth; - int viewHeight; - int viewOffsetX; - int viewOffsetY; - float sceneScaleX; - float sceneScaleY; - float sceneOffsetX; - float sceneOffsetY; - - void update(int outerWidth, int outerHeight, int innerWidth, int innerHeight) { - float scale = Math.min((float)outerWidth / innerWidth, (float)outerHeight / innerHeight); - - viewWidth = (int)Math.ceil(innerWidth * scale); - viewHeight = (int)Math.ceil(innerHeight * scale); - viewOffsetX = (int)((outerWidth - innerWidth * scale) * 0.5f); - viewOffsetY = (int)((outerHeight - innerHeight * scale) * 0.5f); - - sceneScaleX = (innerWidth * scale) / outerWidth; - sceneScaleY = (innerHeight * scale) / outerHeight; - sceneOffsetX = (innerWidth - innerWidth * sceneScaleX) * 0.5f; - sceneOffsetY = (innerHeight - innerHeight * sceneScaleY) * 0.5f; - } -} diff --git a/app/src/main/java/com/winlator/renderer/Viewport.java b/app/src/main/java/com/winlator/renderer/Viewport.java new file mode 100644 index 0000000..dd60c01 --- /dev/null +++ b/app/src/main/java/com/winlator/renderer/Viewport.java @@ -0,0 +1,17 @@ +package com.winlator.renderer; + +public class Viewport { + public int x; + public int y; + public int width; + public int height; + public float aspect; + + public void update(int outerWidth, int outerHeight, int innerWidth, int innerHeight) { + aspect = Math.min((float)outerWidth / innerWidth, (float)outerHeight / innerHeight); + width = (int)Math.ceil(innerWidth * aspect); + height = (int)Math.ceil(innerHeight * aspect); + x = (int)((outerWidth - innerWidth * aspect) * 0.5f); + y = (int)((outerHeight - innerHeight * aspect) * 0.5f); + } +} diff --git a/app/src/main/java/com/winlator/widget/ImagePickerView.java b/app/src/main/java/com/winlator/widget/ImagePickerView.java new file mode 100644 index 0000000..162a77e --- /dev/null +++ b/app/src/main/java/com/winlator/widget/ImagePickerView.java @@ -0,0 +1,119 @@ +package com.winlator.widget; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.RectF; +import android.net.Uri; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import androidx.annotation.Nullable; + +import com.winlator.MainActivity; +import com.winlator.R; +import com.winlator.core.AppUtils; +import com.winlator.core.FileUtils; +import com.winlator.core.ImageUtils; +import com.winlator.core.UnitUtils; +import com.winlator.core.WineThemeManager; +import com.winlator.xenvironment.ImageFs; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Locale; + +public class ImagePickerView extends View implements View.OnClickListener { + private final Bitmap icon; + + public ImagePickerView(Context context) { + this(context, null); + } + + public ImagePickerView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public ImagePickerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_image_picker); + + setBackgroundResource(R.drawable.combo_box); + setClickable(true); + setFocusable(true); + setOnClickListener(this); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + int height = getHeight(); + if (width == 0 || height == 0) return; + + float rectSize = height - UnitUtils.dpToPx(12); + float startX = (width - rectSize) * 0.5f - UnitUtils.dpToPx(16); + float startY = (height - rectSize) * 0.5f; + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + Rect srcRect = new Rect(0, 0, icon.getWidth(), icon.getHeight()); + RectF dstRect = new RectF(startX, startY, startX + rectSize, startY + rectSize); + canvas.drawBitmap(icon, srcRect, dstRect, paint); + } + + @Override + public void onClick(View anchor) { + final Context context = getContext(); + final File userWallpaperFile = WineThemeManager.getUserWallpaperFile(context); + + View view = LayoutInflater.from(context).inflate(R.layout.image_picker_view, null); + ImageView imageView = view.findViewById(R.id.ImageView); + + if (userWallpaperFile.isFile()) { + imageView.setImageBitmap(BitmapFactory.decodeFile(userWallpaperFile.getPath())); + } + else imageView.setImageResource(R.drawable.wallpaper); + + final PopupWindow[] popupWindow = {null}; + View browseButton = view.findViewById(R.id.BTBrowse); + browseButton.setOnClickListener((v) -> { + MainActivity activity = (MainActivity)context; + Intent intent = new Intent(Intent.ACTION_PICK); + intent.setType("image/*"); + activity.setOpenFileCallback((data) -> { + Bitmap bitmap = ImageUtils.getBitmapFromUri(context, data, 1280); + if (bitmap == null) return; + + ImageUtils.save(bitmap, userWallpaperFile, Bitmap.CompressFormat.PNG, 100); + popupWindow[0].dismiss(); + }); + activity.startActivityForResult(intent, MainActivity.OPEN_FILE_REQUEST_CODE); + }); + + View removeButton = view.findViewById(R.id.BTRemove); + if (userWallpaperFile.isFile()) { + removeButton.setVisibility(View.VISIBLE); + removeButton.setOnClickListener((v) -> { + FileUtils.delete(userWallpaperFile); + popupWindow[0].dismiss(); + }); + } + + popupWindow[0] = AppUtils.showPopupWindow(anchor, view, 200, 240); + } +} diff --git a/app/src/main/java/com/winlator/widget/InputControlsView.java b/app/src/main/java/com/winlator/widget/InputControlsView.java index 5f6a5be..a0cd595 100644 --- a/app/src/main/java/com/winlator/widget/InputControlsView.java +++ b/app/src/main/java/com/winlator/widget/InputControlsView.java @@ -12,6 +12,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -25,6 +26,8 @@ import com.winlator.inputcontrols.ExternalController; import com.winlator.inputcontrols.ExternalControllerBinding; import com.winlator.inputcontrols.GamepadState; import com.winlator.math.Mathf; +import com.winlator.math.XForm; +import com.winlator.winhandler.WinHandler; import com.winlator.xserver.Pointer; import com.winlator.xserver.XServer; @@ -417,12 +420,13 @@ public class InputControlsView extends View { public void handleInputEvent(Binding binding, boolean isActionDown, float offset) { if (binding.isGamepad()) { + WinHandler winHandler = xServer != null ? xServer.getWinHandler() : null; GamepadState state = profile.getGamepadState(); int buttonIdx = binding.ordinal() - Binding.GAMEPAD_BUTTON_A.ordinal(); if (buttonIdx <= 11) { state.setPressed(buttonIdx, isActionDown); - if (xServer != null) xServer.getWinHandler().saveGamepadState(state); + if (winHandler != null) winHandler.saveGamepadState(state); } else if (binding == Binding.GAMEPAD_LEFT_THUMB_UP || binding == Binding.GAMEPAD_LEFT_THUMB_DOWN) { state.thumbLY = isActionDown ? offset : 0; @@ -440,6 +444,11 @@ public class InputControlsView extends View { binding == Binding.GAMEPAD_DPAD_DOWN || binding == Binding.GAMEPAD_DPAD_LEFT) { state.dpad[binding.ordinal() - Binding.GAMEPAD_DPAD_UP.ordinal()] = isActionDown; } + + if (winHandler != null) { + ExternalController controller = winHandler.getCurrentController(); + if (controller != null) controller.state.copy(state); + } } else { if (binding == Binding.MOUSE_MOVE_LEFT || binding == Binding.MOUSE_MOVE_RIGHT) { diff --git a/app/src/main/java/com/winlator/widget/MagnifierView.java b/app/src/main/java/com/winlator/widget/MagnifierView.java new file mode 100644 index 0000000..ed981f0 --- /dev/null +++ b/app/src/main/java/com/winlator/widget/MagnifierView.java @@ -0,0 +1,151 @@ +package com.winlator.widget; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.PointF; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; +import androidx.preference.PreferenceManager; + +import com.winlator.R; +import com.winlator.core.Callback; +import com.winlator.core.UnitUtils; +import com.winlator.math.Mathf; + +public class MagnifierView extends FrameLayout { + private final SharedPreferences preferences; + private boolean restoreSavedPosition = true; + private short lastX = 0; + private short lastY = 0; + private Callback zoomButtonCallback; + private Runnable hideButtonCallback; + private TextView textView; + + public MagnifierView(Context context) { + this(context, null); + } + + public MagnifierView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public MagnifierView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + preferences = PreferenceManager.getDefaultSharedPreferences(context); + setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + View contentView = LayoutInflater.from(context).inflate(R.layout.magnifier_view, this, false); + + final PointF startPoint = new PointF(); + final boolean[] isActionDown = {false}; + contentView.findViewById(R.id.BTMove).setOnTouchListener((v, event) -> { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + startPoint.x = event.getX(); + startPoint.y = event.getY(); + isActionDown[0] = true; + break; + case MotionEvent.ACTION_MOVE: + if (isActionDown[0]) { + float newX = getX() + (event.getX() - startPoint.x); + float newY = getY() + (event.getY() - startPoint.y); + movePanel(newX, newY); + } + break; + case MotionEvent.ACTION_UP: + if (isActionDown[0] && lastX > 0 && lastY > 0) { + preferences.edit().putString("magnifier_view", lastX+"|"+lastY).apply(); + } + lastX = 0; + lastY = 0; + isActionDown[0] = false; + break; + } + return true; + }); + + contentView.findViewById(R.id.BTZoomPlus).setOnClickListener((v) -> { + if (zoomButtonCallback != null) zoomButtonCallback.call(0.25f); + }); + + contentView.findViewById(R.id.BTZoomMinus).setOnClickListener((v) -> { + if (zoomButtonCallback != null) zoomButtonCallback.call(-0.25f); + }); + + contentView.findViewById(R.id.BTHide).setOnClickListener((v) -> { + if (hideButtonCallback != null) hideButtonCallback.run(); + }); + + textView = contentView.findViewById(R.id.TextView); + addView(contentView); + } + + public void setZoomValue(float value) { + textView.setText((int)(value * 100)+"%"); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (restoreSavedPosition) { + float x = 1e6f; + float y = 1e6f; + + String config = preferences.getString("magnifier_view", null); + if (config != null) { + try { + String[] parts = config.split("\\|"); + x = Short.parseShort(parts[0]); + y = Short.parseShort(parts[1]); + } + catch (NumberFormatException e) {} + } + + movePanel(x, y); + restoreSavedPosition = false; + } + } + + private void movePanel(float x, float y) { + final int padding = (int)UnitUtils.dpToPx(8); + ViewGroup parent = (ViewGroup)getParent(); + int width = getWidth(); + int height = getHeight(); + int parentWidth = parent.getWidth(); + int parentHeight = parent.getHeight(); + x = Mathf.clamp(x, padding, parentWidth - padding - width); + y = Mathf.clamp(y, padding, parentHeight - padding - height); + setX(x); + setY(y); + lastX = (short)x; + lastY = (short)y; + } + + public Callback getZoomButtonCallback() { + return zoomButtonCallback; + } + + public void setZoomButtonCallback(Callback zoomButtonCallback) { + this.zoomButtonCallback = zoomButtonCallback; + } + + public Runnable getHideButtonCallback() { + return hideButtonCallback; + } + + public void setHideButtonCallback(Runnable hideButtonCallback) { + this.hideButtonCallback = hideButtonCallback; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/winlator/widget/TouchpadView.java b/app/src/main/java/com/winlator/widget/TouchpadView.java index 1e1d971..2465a77 100644 --- a/app/src/main/java/com/winlator/widget/TouchpadView.java +++ b/app/src/main/java/com/winlator/widget/TouchpadView.java @@ -1,22 +1,24 @@ package com.winlator.widget; import android.content.Context; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.winlator.core.AppUtils; import com.winlator.core.CursorLocker; -import com.winlator.core.UnitUtils; +import com.winlator.math.XForm; +import com.winlator.renderer.Viewport; import com.winlator.winhandler.MouseEventFlags; import com.winlator.winhandler.WinHandler; import com.winlator.xserver.Pointer; import com.winlator.xserver.XServer; public class TouchpadView extends View { - private static final float DEFAULT_SENSITIVITY = 2.5f; private static final byte MAX_FINGERS = 4; - private static final short MAX_TWO_FINGERS_SCROLL_DISTANCE = 150; + private static final short MAX_TWO_FINGERS_SCROLL_DISTANCE = 350; private static final byte MAX_TAP_TRAVEL_DISTANCE = 10; private final Finger[] fingers = new Finger[MAX_FINGERS]; private byte numFingers = 0; @@ -29,6 +31,7 @@ public class TouchpadView extends View { private boolean scrolling = false; private final XServer xServer; private Runnable fourFingersTapCallback; + private final float[] xform = XForm.getInstance(); public TouchpadView(Context context, XServer xServer) { super(context); @@ -37,6 +40,25 @@ public class TouchpadView extends View { setClickable(true); setFocusable(false); setFocusableInTouchMode(false); + updateXform(AppUtils.getScreenWidth(), AppUtils.getScreenHeight(), xServer.screenInfo.width, xServer.screenInfo.height); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + updateXform(w, h, xServer.screenInfo.width, xServer.screenInfo.height); + } + + private void updateXform(int outerWidth, int outerHeight, int innerWidth, int innerHeight) { + Viewport viewport = new Viewport(); + viewport.update(outerWidth, outerHeight, innerWidth, innerHeight); + + float invAspect = 1.0f / viewport.aspect; + if (!xServer.getRenderer().isFullscreen()) { + XForm.makeTranslation(xform, -viewport.x, -viewport.y); + XForm.scale(xform, invAspect, invAspect); + } + else XForm.makeScale(xform, invAspect, invAspect); } private class Finger { @@ -49,25 +71,27 @@ public class TouchpadView extends View { private final long touchTime; public Finger(float x, float y) { - this.x = this.startX = this.lastX = UnitUtils.pxToDp(x); - this.y = this.startY = this.lastY = UnitUtils.pxToDp(y); + float[] transformedPoint = XForm.transformPoint(xform, x, y); + this.x = this.startX = this.lastX = transformedPoint[0]; + this.y = this.startY = this.lastY = transformedPoint[1]; touchTime = System.currentTimeMillis(); } public void update(float x, float y) { lastX = this.x; lastY = this.y; - this.x = UnitUtils.pxToDp(x); - this.y = UnitUtils.pxToDp(y); + float[] transformedPoint = XForm.transformPoint(xform, x, y); + this.x = transformedPoint[0]; + this.y = transformedPoint[1]; } private int deltaX() { - float dx = (x - lastX) * DEFAULT_SENSITIVITY * sensitivity; + float dx = (x - lastX) * sensitivity; return (int)(x <= lastX ? Math.floor(dx) : Math.ceil(dx)); } private int deltaY() { - float dy = (y - lastY) * DEFAULT_SENSITIVITY * sensitivity; + float dy = (y - lastY) * sensitivity; return (int)(y <= lastY ? Math.floor(dy) : Math.ceil(dy)); } @@ -90,23 +114,30 @@ public class TouchpadView extends View { switch (actionMasked) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) return true; scrollAccumY = 0; scrolling = false; fingers[pointerId] = new Finger(event.getX(actionIndex), event.getY(actionIndex)); numFingers++; break; case MotionEvent.ACTION_MOVE: - for (byte i = 0; i < MAX_FINGERS; i++) { - if (fingers[i] != null) { - int pointerIndex = event.findPointerIndex(i); - if (pointerIndex >= 0) { - fingers[i].update(event.getX(pointerIndex), event.getY(pointerIndex)); - handleFingerMove(fingers[i]); - } - else { - handleFingerUp(fingers[i]); - fingers[i] = null; - numFingers--; + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + float[] transformedPoint = XForm.transformPoint(xform, event.getX(), event.getY()); + xServer.injectPointerMove((int)transformedPoint[0], (int)transformedPoint[1]); + } + else { + for (byte i = 0; i < MAX_FINGERS; i++) { + if (fingers[i] != null) { + int pointerIndex = event.findPointerIndex(i); + if (pointerIndex >= 0) { + fingers[i].update(event.getX(pointerIndex), event.getY(pointerIndex)); + handleFingerMove(fingers[i]); + } + else { + handleFingerUp(fingers[i]); + fingers[i] = null; + numFingers--; + } } } } @@ -121,6 +152,7 @@ public class TouchpadView extends View { } break; case MotionEvent.ACTION_CANCEL: + for (byte i = 0; i < MAX_FINGERS; i++) fingers[i] = null; numFingers = 0; break; } @@ -156,10 +188,10 @@ public class TouchpadView extends View { Finger finger2 = numFingers == 2 ? findSecondFinger(finger1) : null; if (finger2 != null) { - float lastDistance = (float)Math.hypot(finger1.lastX - finger2.lastX, finger1.lastY - finger2.lastY); - float currDistance = (float)Math.hypot(finger1.x - finger2.x, finger1.y - finger2.y); + final float resolutionScale = 1000.0f / Math.min(xServer.screenInfo.width, xServer.screenInfo.height); + float currDistance = (float)Math.hypot(finger1.x - finger2.x, finger1.y - finger2.y) * resolutionScale; - if (currDistance < MAX_TWO_FINGERS_SCROLL_DISTANCE && Math.abs(currDistance - lastDistance) < 20) { + if (currDistance < MAX_TWO_FINGERS_SCROLL_DISTANCE) { scrollAccumY += ((finger1.y + finger2.y) * 0.5f) - (finger1.lastY + finger2.lastY) * 0.5f; if (scrollAccumY < -100) { @@ -255,4 +287,49 @@ public class TouchpadView extends View { public void setFourFingersTapCallback(Runnable fourFingersTapCallback) { this.fourFingersTapCallback = fourFingersTapCallback; } + + public boolean onExternalMouseEvent(MotionEvent event) { + boolean handled = false; + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + int actionButton = event.getActionButton(); + switch (event.getAction()) { + case MotionEvent.ACTION_BUTTON_PRESS: + if (actionButton == MotionEvent.BUTTON_PRIMARY) { + xServer.injectPointerButtonPress(Pointer.Button.BUTTON_LEFT); + } + else if (actionButton == MotionEvent.BUTTON_SECONDARY) { + xServer.injectPointerButtonPress(Pointer.Button.BUTTON_RIGHT); + } + handled = true; + break; + case MotionEvent.ACTION_BUTTON_RELEASE: + if (actionButton == MotionEvent.BUTTON_PRIMARY) { + xServer.injectPointerButtonRelease(Pointer.Button.BUTTON_LEFT); + } + else if (actionButton == MotionEvent.BUTTON_SECONDARY) { + xServer.injectPointerButtonRelease(Pointer.Button.BUTTON_RIGHT); + } + handled = true; + break; + case MotionEvent.ACTION_HOVER_MOVE: + float[] transformedPoint = XForm.transformPoint(xform, event.getX(), event.getY()); + xServer.injectPointerMove((int)transformedPoint[0], (int)transformedPoint[1]); + handled = true; + break; + case MotionEvent.ACTION_SCROLL: + float scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL); + if (scrollY <= -1.0f) { + xServer.injectPointerButtonPress(Pointer.Button.BUTTON_SCROLL_DOWN); + xServer.injectPointerButtonRelease(Pointer.Button.BUTTON_SCROLL_DOWN); + } + else if (scrollY >= 1.0f) { + xServer.injectPointerButtonPress(Pointer.Button.BUTTON_SCROLL_UP); + xServer.injectPointerButtonRelease(Pointer.Button.BUTTON_SCROLL_UP); + } + handled = true; + break; + } + } + return handled; + } } diff --git a/app/src/main/java/com/winlator/winhandler/RequestCodes.java b/app/src/main/java/com/winlator/winhandler/RequestCodes.java index 0552c36..ee981a5 100644 --- a/app/src/main/java/com/winlator/winhandler/RequestCodes.java +++ b/app/src/main/java/com/winlator/winhandler/RequestCodes.java @@ -13,4 +13,5 @@ abstract class RequestCodes { public static final byte GET_GAMEPAD_STATE = 9; public static final byte RELEASE_GAMEPAD = 10; public static final byte KEYBOARD_EVENT = 11; + public static final byte BRING_TO_FRONT = 12; } \ No newline at end of file diff --git a/app/src/main/java/com/winlator/winhandler/TaskManagerDialog.java b/app/src/main/java/com/winlator/winhandler/TaskManagerDialog.java index 7b06c3b..45dcd25 100644 --- a/app/src/main/java/com/winlator/winhandler/TaskManagerDialog.java +++ b/app/src/main/java/com/winlator/winhandler/TaskManagerDialog.java @@ -13,11 +13,11 @@ import android.widget.TextView; import com.winlator.R; import com.winlator.XServerDisplayActivity; +import com.winlator.contentdialog.ContentDialog; import com.winlator.core.CPUStatus; import com.winlator.core.ProcessHelper; import com.winlator.core.StringUtils; import com.winlator.widget.CPUListView; -import com.winlator.contentdialog.ContentDialog; import java.util.Timer; import java.util.TimerTask; @@ -73,12 +73,17 @@ public class TaskManagerDialog extends ContentDialog implements OnGetProcessInfo listItemMenu.inflate(R.menu.process_popup_menu); listItemMenu.setOnMenuItemClickListener((menuItem) -> { int itemId = menuItem.getItemId(); + final WinHandler winHandler = activity.getWinHandler(); if (itemId == R.id.process_affinity) { showProcessorAffinityDialog(processInfo); } + else if (itemId == R.id.bring_to_front) { + winHandler.bringToFront(processInfo.name); + dismiss(); + } else if (itemId == R.id.process_end) { ContentDialog.confirm(activity, R.string.do_you_want_to_end_this_process, () -> { - activity.getWinHandler().killProcess(processInfo.name); + winHandler.killProcess(processInfo.name); }); } return true; diff --git a/app/src/main/java/com/winlator/winhandler/WinHandler.java b/app/src/main/java/com/winlator/winhandler/WinHandler.java index f2fb81f..de5ec73 100644 --- a/app/src/main/java/com/winlator/winhandler/WinHandler.java +++ b/app/src/main/java/com/winlator/winhandler/WinHandler.java @@ -138,6 +138,22 @@ public class WinHandler { }); } + public void bringToFront(final String processName) { + bringToFront(processName, false); + } + + public void bringToFront(final String processName, final boolean reverse) { + addAction(() -> { + sendData.rewind(); + sendData.put(RequestCodes.BRING_TO_FRONT); + byte[] bytes = processName.getBytes(); + sendData.putInt(bytes.length); + sendData.put(bytes); + sendData.put((byte)(reverse ? 1 : 0)); + sendPacket(CLIENT_PORT); + }); + } + private void addAction(Runnable action) { synchronized (actions) { actions.add(action); @@ -186,6 +202,10 @@ public class WinHandler { switch (requestCode) { case RequestCodes.INIT: { initReceived = true; + + synchronized (actions) { + actions.notify(); + } break; } case RequestCodes.GET_PROCESS: { @@ -344,4 +364,8 @@ public class WinHandler { public void setDInputMapperType(byte dinputMapperType) { this.dinputMapperType = dinputMapperType; } + + public ExternalController getCurrentController() { + return currentController; + } } diff --git a/app/src/main/java/com/winlator/xconnector/XConnectorEpoll.java b/app/src/main/java/com/winlator/xconnector/XConnectorEpoll.java index fd4990d..d388ab4 100644 --- a/app/src/main/java/com/winlator/xconnector/XConnectorEpoll.java +++ b/app/src/main/java/com/winlator/xconnector/XConnectorEpoll.java @@ -126,12 +126,6 @@ public class XConnectorEpoll implements Runnable { return connectedClients.get(fd); } - public ArrayList getConnectedClients() { - ArrayList clients = new ArrayList<>(); - for (int i = 0; i < connectedClients.size(); i++) clients.add(connectedClients.valueAt(i)); - return clients; - } - public void killConnection(Client client) { client.connected = false; connectionHandler.handleConnectionShutdown(client); diff --git a/app/src/main/java/com/winlator/xenvironment/ImageFs.java b/app/src/main/java/com/winlator/xenvironment/ImageFs.java index 3163f28..b46c7e5 100644 --- a/app/src/main/java/com/winlator/xenvironment/ImageFs.java +++ b/app/src/main/java/com/winlator/xenvironment/ImageFs.java @@ -14,6 +14,7 @@ public class ImageFs { public static final String USER = "xuser"; public static final String HOME_PATH = "/home/"+USER; public static final String CACHE_PATH = "/home/"+USER+"/.cache"; + public static final String CONFIG_PATH = "/home/"+USER+"/.config"; public static final String WINEPREFIX = "/home/"+USER+"/.wine"; private final File rootDir; private String winePath = "/opt/wine"; diff --git a/app/src/main/java/com/winlator/xenvironment/ImageFsInstaller.java b/app/src/main/java/com/winlator/xenvironment/ImageFsInstaller.java new file mode 100644 index 0000000..a136623 --- /dev/null +++ b/app/src/main/java/com/winlator/xenvironment/ImageFsInstaller.java @@ -0,0 +1,179 @@ +package com.winlator.xenvironment; + +import android.content.Context; + +import androidx.appcompat.app.AppCompatActivity; + +import com.winlator.MainActivity; +import com.winlator.R; +import com.winlator.SettingsFragment; +import com.winlator.container.Container; +import com.winlator.container.ContainerManager; +import com.winlator.core.AppUtils; +import com.winlator.core.DownloadProgressDialog; +import com.winlator.core.FileUtils; +import com.winlator.core.PreloaderDialog; +import com.winlator.core.TarCompressorUtils; +import com.winlator.xenvironment.ImageFs; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; + +public abstract class ImageFsInstaller { + public static final byte LATEST_VERSION = 4; + + private static void resetContainerImgVersions(Context context) { + ContainerManager manager = new ContainerManager(context); + for (Container container : manager.getContainers()) { + container.putExtra("imgVersion", null); + container.saveData(); + } + } + + private static void installFromAssets(final MainActivity activity) { + AppUtils.keepScreenOn(activity); + ImageFs imageFs = ImageFs.find(activity); + final File rootDir = imageFs.getRootDir(); + + SettingsFragment.resetBox86_64Version(activity); + + final DownloadProgressDialog dialog = new DownloadProgressDialog(activity); + dialog.show(R.string.installing_system_files); + Executors.newSingleThreadExecutor().execute(() -> { + clearRootDir(rootDir); + final byte compressionRatio = 22; + final long contentLength = (long)(FileUtils.getSize(activity, "imagefs.txz") * (100.0f / compressionRatio)); + AtomicLong totalSizeRef = new AtomicLong(); + + boolean success = TarCompressorUtils.extract(TarCompressorUtils.Type.XZ, activity, "imagefs.txz", rootDir, (file, size) -> { + if (size > 0) { + long totalSize = totalSizeRef.addAndGet(size); + final int progress = (int)(((float)totalSize / contentLength) * 100); + activity.runOnUiThread(() -> dialog.setProgress(progress)); + } + return file; + }); + + if (success) { + imageFs.createImgVersionFile(LATEST_VERSION); + resetContainerImgVersions(activity); + } + else AppUtils.showToast(activity, R.string.unable_to_install_system_files); + + dialog.closeOnUiThread(); + }); + } + + public static void installIfNeeded(final MainActivity activity) { + ImageFs imageFs = ImageFs.find(activity); + if (!imageFs.isValid() || imageFs.getVersion() < LATEST_VERSION) installFromAssets(activity); + } + + private static void clearOptDir(File optDir) { + File[] files = optDir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.getName().equals("installed-wine")) continue; + FileUtils.delete(file); + } + } + } + + private static void clearRootDir(File rootDir) { + if (rootDir.isDirectory()) { + File[] files = rootDir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + String name = file.getName(); + if (name.equals("home") || name.equals("opt")) { + if (name.equals("opt")) clearOptDir(file); + continue; + } + } + FileUtils.delete(file); + } + } + } + else rootDir.mkdirs(); + } + + public static void generateCompactContainerPattern(final AppCompatActivity activity) { + PreloaderDialog preloaderDialog = new PreloaderDialog(activity); + preloaderDialog.show(R.string.loading); + Executors.newSingleThreadExecutor().execute(() -> { + File[] srcFiles, dstFiles; + File rootDir = ImageFs.find(activity).getRootDir(); + File wineSystem32Dir = new File(rootDir, "/opt/wine/lib/wine/x86_64-windows"); + File wineSysWoW64Dir = new File(rootDir, "/opt/wine/lib/wine/i386-windows"); + + File containerPatternDir = new File(activity.getCacheDir(), "container_pattern"); + FileUtils.delete(containerPatternDir); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, activity, "container_pattern.tzst", containerPatternDir); + + File containerSystem32Dir = new File(containerPatternDir, ".wine/drive_c/windows/system32"); + File containerSysWoW64Dir = new File(containerPatternDir, ".wine/drive_c/windows/syswow64"); + + dstFiles = containerSystem32Dir.listFiles(); + srcFiles = wineSystem32Dir.listFiles(); + + ArrayList system32Files = new ArrayList<>(); + ArrayList syswow64Files = new ArrayList<>(); + + for (File dstFile : dstFiles) { + for (File srcFile : srcFiles) { + if (dstFile.getName().equals(srcFile.getName())) { + if (FileUtils.contentEquals(srcFile, dstFile)) system32Files.add(srcFile.getName()); + break; + } + } + } + + dstFiles = containerSysWoW64Dir.listFiles(); + srcFiles = wineSysWoW64Dir.listFiles(); + + for (File dstFile : dstFiles) { + for (File srcFile : srcFiles) { + if (dstFile.getName().equals(srcFile.getName())) { + if (FileUtils.contentEquals(srcFile, dstFile)) syswow64Files.add(srcFile.getName()); + break; + } + } + } + + try { + JSONObject data = new JSONObject(); + + JSONArray system32JSONArray = new JSONArray(); + for (String name : system32Files) { + FileUtils.delete(new File(containerSystem32Dir, name)); + system32JSONArray.put(name); + } + data.put("system32", system32JSONArray); + + JSONArray syswow64JSONArray = new JSONArray(); + for (String name : syswow64Files) { + FileUtils.delete(new File(containerSysWoW64Dir, name)); + syswow64JSONArray.put(name); + } + data.put("syswow64", syswow64JSONArray); + + FileUtils.writeString(new File(activity.getCacheDir(), "common_dlls.json"), data.toString()); + + File outputFile = new File(activity.getCacheDir(), "container_pattern.tzst"); + FileUtils.delete(outputFile); + TarCompressorUtils.compress(TarCompressorUtils.Type.ZSTD, new File(containerPatternDir, ".wine"), outputFile, 22); + + FileUtils.delete(containerPatternDir); + preloaderDialog.closeOnUiThread(); + } + catch (JSONException e) {} + }); + } +} diff --git a/app/src/main/java/com/winlator/xenvironment/components/GuestProgramLauncherComponent.java b/app/src/main/java/com/winlator/xenvironment/components/GuestProgramLauncherComponent.java index b42a646..0f57928 100644 --- a/app/src/main/java/com/winlator/xenvironment/components/GuestProgramLauncherComponent.java +++ b/app/src/main/java/com/winlator/xenvironment/components/GuestProgramLauncherComponent.java @@ -7,13 +7,13 @@ import android.os.Process; import androidx.preference.PreferenceManager; import com.winlator.MainActivity; -import com.winlator.SettingsFragment; import com.winlator.box86_64.Box86_64Preset; import com.winlator.box86_64.Box86_64PresetManager; import com.winlator.core.Callback; import com.winlator.core.EnvVars; import com.winlator.core.ProcessHelper; -import com.winlator.core.TarZstdUtils; +import com.winlator.core.TarCompressorUtils; +import com.winlator.core.DefaultVersion; import com.winlator.xconnector.UnixSocketConfig; import com.winlator.xenvironment.EnvironmentComponent; import com.winlator.xenvironment.ImageFs; @@ -25,7 +25,6 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { private static int pid = -1; private String[] bindingPaths; private EnvVars envVars; - private String cpuList; private String box86Preset = Box86_64Preset.COMPATIBILITY; private String box64Preset = Box86_64Preset.COMPATIBILITY; private Callback terminationCallback; @@ -82,14 +81,6 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { this.envVars = envVars; } - public String getCpuList() { - return cpuList; - } - - public void setCpuList(String cpuList) { - this.cpuList = cpuList; - } - public String getBox86Preset() { return box86Preset; } @@ -127,15 +118,20 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { envVars.put("ANDROID_SYSVSHM_SERVER", UnixSocketConfig.SYSVSHM_SERVER_PATH); if (this.envVars != null) envVars.putAll(this.envVars); - File shmDir = new File(rootDir, "/tmp/shm"); - shmDir.mkdirs(); + boolean bindSHM = envVars.get("WINEESYNC").equals("1"); String command = nativeLibraryDir+"/libproot.so"; command += " --kill-on-exit"; command += " --rootfs="+rootDir; command += " --cwd="+ImageFs.HOME_PATH; command += " --bind=/dev"; - command += " --bind="+shmDir.getAbsolutePath()+":/dev/shm"; + + if (bindSHM) { + File shmDir = new File(rootDir, "/tmp/shm"); + shmDir.mkdirs(); + command += " --bind="+shmDir.getAbsolutePath()+":/dev/shm"; + } + command += " --bind=/proc"; command += " --bind=/sys"; @@ -143,7 +139,7 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { for (String path : bindingPaths) command += " --bind="+(new File(path)).getAbsolutePath(); } - command += " /bin/env "+envVars.toEscapedString()+(cpuList != null ? " taskset -c "+cpuList : "")+" box64 "+guestExecutable; + command += " /usr/bin/env "+envVars.toEscapedString()+" box64 "+guestExecutable; envVars.clear(); envVars.put("PROOT_TMP_DIR", tmpDir); @@ -163,19 +159,19 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { ImageFs imageFs = environment.getImageFs(); Context context = environment.getContext(); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - String box86Version = preferences.getString("box86_version", SettingsFragment.DEFAULT_BOX86_VERSION); - String box64Version = preferences.getString("box64_version", SettingsFragment.DEFAULT_BOX64_VERSION); + String box86Version = preferences.getString("box86_version", DefaultVersion.BOX86); + String box64Version = preferences.getString("box64_version", DefaultVersion.BOX64); String currentBox86Version = preferences.getString("current_box86_version", ""); String currentBox64Version = preferences.getString("current_box64_version", ""); File rootDir = imageFs.getRootDir(); if (!box86Version.equals(currentBox86Version)) { - TarZstdUtils.extract(context, "box86_64/box86-"+box86Version+".tzst", rootDir); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, context, "box86_64/box86-"+box86Version+".tzst", rootDir); preferences.edit().putString("current_box86_version", box86Version).apply(); } if (!box64Version.equals(currentBox64Version)) { - TarZstdUtils.extract(context, "box86_64/box64-"+box64Version+".tzst", rootDir); + TarCompressorUtils.extract(TarCompressorUtils.Type.ZSTD, context, "box86_64/box64-"+box64Version+".tzst", rootDir); preferences.edit().putString("current_box64_version", box64Version).apply(); } } @@ -183,6 +179,9 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { private void addBox86EnvVars(EnvVars envVars) { envVars.put("BOX86_NOBANNER", "1"); envVars.put("BOX86_DYNAREC", "1"); + + if (MainActivity.DEBUG_LEVEL >= 3) envVars.put("BOX86_LOG", MainActivity.DEBUG_LEVEL == 3 ? "1" : "2"); + envVars.putAll(Box86_64PresetManager.getEnvVars("box86", environment.getContext(), box86Preset)); envVars.put("BOX86_X11GLX", "1"); envVars.put("BOX86_NORCFILES", "1"); @@ -191,6 +190,9 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent { private void addBox64EnvVars(EnvVars envVars) { envVars.put("BOX64_NOBANNER", "1"); envVars.put("BOX64_DYNAREC", "1"); + + if (MainActivity.DEBUG_LEVEL >= 3) envVars.put("BOX64_LOG", MainActivity.DEBUG_LEVEL == 3 ? "1" : "2"); + envVars.putAll(Box86_64PresetManager.getEnvVars("box64", environment.getContext(), box64Preset)); envVars.put("BOX64_X11GLX", "1"); envVars.put("BOX64_NORCFILES", "1"); diff --git a/app/src/main/java/com/winlator/xenvironment/components/VirGLRendererComponent.java b/app/src/main/java/com/winlator/xenvironment/components/VirGLRendererComponent.java index 615fa57..493c433 100644 --- a/app/src/main/java/com/winlator/xenvironment/components/VirGLRendererComponent.java +++ b/app/src/main/java/com/winlator/xenvironment/components/VirGLRendererComponent.java @@ -83,13 +83,6 @@ public class VirGLRendererComponent extends EnvironmentComponent implements Conn @Override public void handleNewConnection(Client client) { getSharedEGLContext(); - - ArrayList clients = connector.getConnectedClients(); - if (!clients.isEmpty()) { - long clientPtr = (long)clients.get(clients.size()-1).getTag(); - destroyRenderer(clientPtr); - } - long clientPtr = handleNewConnection(client.clientSocket.fd); client.setTag(clientPtr); } diff --git a/app/src/main/java/com/winlator/xserver/extensions/PresentExtension.java b/app/src/main/java/com/winlator/xserver/extensions/PresentExtension.java index 46f6d11..82aaa1f 100644 --- a/app/src/main/java/com/winlator/xserver/extensions/PresentExtension.java +++ b/app/src/main/java/com/winlator/xserver/extensions/PresentExtension.java @@ -28,6 +28,7 @@ import java.io.IOException; public class PresentExtension implements Extension { public static final byte MAJOR_OPCODE = -103; + private static final int FAKE_INTERVAL = 1000000 / 60; public enum Kind {PIXMAP, MSC_NOTIFY} public enum Mode {COPY, FLIP, SKIP} private final SparseArray events = new SparseArray<>(); @@ -124,9 +125,8 @@ public class PresentExtension implements Extension { Drawable content = window.getContent(); if (content.visual.depth != pixmap.drawable.visual.depth) throw new BadMatch(); - final int fakeInterval = 1000000 / 60; long ust = System.nanoTime() / 1000; - long msc = ust / fakeInterval; + long msc = ust / FAKE_INTERVAL; synchronized (content.renderLock) { content.copyArea((short)0, (short)0, xOff, yOff, pixmap.drawable.width, pixmap.drawable.height, pixmap.drawable); diff --git a/app/src/main/jniLibs/arm64-v8a/libepoxy.so b/app/src/main/jniLibs/arm64-v8a/libepoxy.so deleted file mode 100644 index a858ffa..0000000 Binary files a/app/src/main/jniLibs/arm64-v8a/libepoxy.so and /dev/null differ diff --git a/app/src/main/jniLibs/arm64-v8a/libproot-loader.so b/app/src/main/jniLibs/arm64-v8a/libproot-loader.so deleted file mode 100644 index 4b70f21..0000000 Binary files a/app/src/main/jniLibs/arm64-v8a/libproot-loader.so and /dev/null differ diff --git a/app/src/main/jniLibs/arm64-v8a/libproot-loader32.so b/app/src/main/jniLibs/arm64-v8a/libproot-loader32.so index eb559c9..b23c16f 100644 Binary files a/app/src/main/jniLibs/arm64-v8a/libproot-loader32.so and b/app/src/main/jniLibs/arm64-v8a/libproot-loader32.so differ diff --git a/app/src/main/jniLibs/arm64-v8a/libproot.so b/app/src/main/jniLibs/arm64-v8a/libproot.so deleted file mode 100644 index 0404dcb..0000000 Binary files a/app/src/main/jniLibs/arm64-v8a/libproot.so and /dev/null differ diff --git a/app/src/main/jniLibs/arm64-v8a/libvirglrenderer.so b/app/src/main/jniLibs/arm64-v8a/libvirglrenderer.so deleted file mode 100644 index 0a8678f..0000000 Binary files a/app/src/main/jniLibs/arm64-v8a/libvirglrenderer.so and /dev/null differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libepoxy.so b/app/src/main/jniLibs/armeabi-v7a/libepoxy.so deleted file mode 100644 index 33480b6..0000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libepoxy.so and /dev/null differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libproot-loader.so b/app/src/main/jniLibs/armeabi-v7a/libproot-loader.so deleted file mode 100644 index 3bf74fc..0000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libproot-loader.so and /dev/null differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libproot.so b/app/src/main/jniLibs/armeabi-v7a/libproot.so deleted file mode 100644 index d2b4350..0000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libproot.so and /dev/null differ diff --git a/app/src/main/jniLibs/armeabi-v7a/libvirglrenderer.so b/app/src/main/jniLibs/armeabi-v7a/libvirglrenderer.so deleted file mode 100644 index 321e26f..0000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libvirglrenderer.so and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/icon_hide.png b/app/src/main/res/drawable-hdpi/icon_hide.png new file mode 100644 index 0000000..d82a6e9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_hide.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_image_picker.png b/app/src/main/res/drawable-hdpi/icon_image_picker.png new file mode 100644 index 0000000..486ed17 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_image_picker.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_magnifier.png b/app/src/main/res/drawable-hdpi/icon_magnifier.png new file mode 100644 index 0000000..e43f4e2 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_magnifier.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_move.png b/app/src/main/res/drawable-hdpi/icon_move.png new file mode 100644 index 0000000..ce67014 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_move.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_zoom_minus.png b/app/src/main/res/drawable-hdpi/icon_zoom_minus.png new file mode 100644 index 0000000..fa3b9e0 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_zoom_minus.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_zoom_plus.png b/app/src/main/res/drawable-hdpi/icon_zoom_plus.png new file mode 100644 index 0000000..84ba4df Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_zoom_plus.png differ diff --git a/app/src/main/res/drawable-hdpi/remove_button.png b/app/src/main/res/drawable-hdpi/remove_button.png new file mode 100644 index 0000000..7b71b6e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/remove_button.png differ diff --git a/app/src/main/res/drawable-hdpi/wallpaper.png b/app/src/main/res/drawable-hdpi/wallpaper.png new file mode 100644 index 0000000..735e9b4 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/wallpaper.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_popup_menu_bring_to_front.png b/app/src/main/res/drawable-xxhdpi/icon_popup_menu_bring_to_front.png new file mode 100644 index 0000000..ee071c2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_popup_menu_bring_to_front.png differ diff --git a/app/src/main/res/layout/about_dialog.xml b/app/src/main/res/layout/about_dialog.xml index bd94a81..dcc5953 100644 --- a/app/src/main/res/layout/about_dialog.xml +++ b/app/src/main/res/layout/about_dialog.xml @@ -42,13 +42,6 @@ android:layout_height="wrap_content" android:textSize="14sp" android:textColor="#9e9e9e" /> - - - + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="center_vertical"> + + + + + + android:text="@string/win_components" /> + + + + + + - + android:entries="@array/desktop_background_type_entries" /> - - - - - - + android:visibility="gone" /> - - - - + @@ -330,7 +345,8 @@ + android:layout_width="match_parent" + android:entries="@array/video_memory_size_entries" /> @@ -352,7 +368,7 @@ - + -