diff --git a/.gitignore b/.gitignore index 9f3c00dd2..4a92d1f81 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,8 @@ target/ /.rust/ rusti.sh /examples/**/target - Cargo.lock +*/**/dist +*/**/node_modules +backends/conrod_crayon/tools/wasm-template/package-lock.json +.travis.yml \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index bf624ff20..e3e94c751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ members = [ "backends/conrod_glium", "backends/conrod_piston", "backends/conrod_vulkano", + "backends/conrod_crayon", ] diff --git a/README.md b/README.md index 6c01e611b..11091fb04 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Conrod [![Build Status](https://travis-ci.org/PistonDevelopers/conrod.svg?branch=master)](https://travis-ci.org/PistonDevelopers/conrod) [![Crates.io](https://img.shields.io/crates/l/conrod_core.svg)](https://github.com/PistonDevelopers/conrod/blob/master/LICENSE) - +[![Build Status](https://dev.azure.com/K1720055F/conrod/_apis/build/status/alanpoon.conrod%20(1)?branchName=master)](https://dev.azure.com/K1720055F/conrod/_build/latest?definitionId=3&branchName=master) An easy-to-use, 2D GUI library written entirely in Rust. Guide diff --git a/assets/crayon/assets/Oswald-Heavy.ttf b/assets/crayon/assets/Oswald-Heavy.ttf new file mode 100644 index 000000000..0db6cf052 Binary files /dev/null and b/assets/crayon/assets/Oswald-Heavy.ttf differ diff --git a/assets/crayon/assets/Oswald-Heavy.ttf.meta.toml b/assets/crayon/assets/Oswald-Heavy.ttf.meta.toml new file mode 100644 index 000000000..fd96a680f --- /dev/null +++ b/assets/crayon/assets/Oswald-Heavy.ttf.meta.toml @@ -0,0 +1,10 @@ +name = 'Oswald-Heavy.ttf' +uuid = 'b148e135-5e22-451b-92f6-3671e60ac865' + +[[resources]] +type = 'Bytes' +name = 'Oswald-Heavy.ttf' +uuid = '26aa066c-5983-4da8-884c-4ac7bf9c49f6' + +[params] +type = 'Bytes' diff --git a/assets/crayon/assets/cornell_box.obj b/assets/crayon/assets/cornell_box.obj new file mode 100644 index 000000000..fb00a8ddd --- /dev/null +++ b/assets/crayon/assets/cornell_box.obj @@ -0,0 +1,118 @@ +# The original Cornell Box in OBJ format. +# Note that the real box is not a perfect cube, so +# the faces are imperfect in this data set. +# +# Created by Guedis Cardenas and Morgan McGuire at Williams College, 2011 +# Released into the Public Domain. +# +# http://graphics.cs.williams.edu/data +# http://www.graphics.cornell.edu/online/box/data.html +# + +## Object floor +v -1.01 0.00 0.99 +v 1.00 0.00 0.99 +v 1.00 0.00 -1.04 +v -0.99 0.00 -1.04 + +g floor +usemtl floor +f -4 -3 -2 -1 + +## Object backwall +v -0.99 0.00 -1.04 +v 1.00 0.00 -1.04 +v 1.00 1.99 -1.04 +v -1.02 1.99 -1.04 + +## Object shortBox +g shortBox +usemtl shortBox + +# Top Face +v 0.53 0.60 0.75 +v 0.70 0.60 0.17 +v 0.13 0.60 0.00 +v -0.05 0.60 0.57 +f -4 -3 -2 -1 + +# Left Face +v -0.05 0.00 0.57 +v -0.05 0.60 0.57 +v 0.13 0.60 0.00 +v 0.13 0.00 0.00 +f -4 -3 -2 -1 + +# Front Face +v 0.53 0.00 0.75 +v 0.53 0.60 0.75 +v -0.05 0.60 0.57 +v -0.05 0.00 0.57 +f -4 -3 -2 -1 + +# Right Face +v 0.70 0.00 0.17 +v 0.70 0.60 0.17 +v 0.53 0.60 0.75 +v 0.53 0.00 0.75 +f -4 -3 -2 -1 + +# Back Face +v 0.13 0.00 0.00 +v 0.13 0.60 0.00 +v 0.70 0.60 0.17 +v 0.70 0.00 0.17 +f -4 -3 -2 -1 + +# Bottom Face +v 0.53 0.00 0.75 +v 0.70 0.00 0.17 +v 0.13 0.00 0.00 +v -0.05 0.00 0.57 +f -12 -11 -10 -9 + +## Object tallBox +g tallBox +usemtl tallBox + +# Top Face +v -0.53 1.20 0.09 +v 0.04 1.20 -0.09 +v -0.14 1.20 -0.67 +v -0.71 1.20 -0.49 +f -4 -3 -2 -1 + +# Left Face +v -0.53 0.00 0.09 +v -0.53 1.20 0.09 +v -0.71 1.20 -0.49 +v -0.71 0.00 -0.49 +f -4 -3 -2 -1 + +# Back Face +v -0.71 0.00 -0.49 +v -0.71 1.20 -0.49 +v -0.14 1.20 -0.67 +v -0.14 0.00 -0.67 +f -4 -3 -2 -1 + +# Right Face +v -0.14 0.00 -0.67 +v -0.14 1.20 -0.67 +v 0.04 1.20 -0.09 +v 0.04 0.00 -0.09 +f -4 -3 -2 -1 + +# Front Face +v 0.04 0.00 -0.09 +v 0.04 1.20 -0.09 +v -0.53 1.20 0.09 +v -0.53 0.00 0.09 +f -4 -3 -2 -1 + +# Bottom Face +v -0.53 0.00 0.09 +v 0.04 0.00 -0.09 +v -0.14 0.00 -0.67 +v -0.71 0.00 -0.49 +f -8 -7 -6 -5 \ No newline at end of file diff --git a/assets/crayon/assets/cornell_box.obj.meta.toml b/assets/crayon/assets/cornell_box.obj.meta.toml new file mode 100644 index 000000000..69ad076d8 --- /dev/null +++ b/assets/crayon/assets/cornell_box.obj.meta.toml @@ -0,0 +1,31 @@ +name = 'cornell_box.obj' +uuid = '13b56f8c-7724-4313-a5ff-d870db23b243' + +[[resources]] +type = 'Prefab' +name = 'cornell_box.obj' +uuid = '64cd2e4e-1949-4718-ad79-bec5f4cd5600' + +[[resources]] +type = 'Mesh' +name = 'floor' +uuid = '6f0c4447-040f-47c2-8f48-bb8284df5678' + +[[resources]] +type = 'Mesh' +name = 'shortBox' +uuid = 'd0cb0ac2-ac69-49c9-98cb-f5841751b3fd' + +[[resources]] +type = 'Mesh' +name = 'tallBox' +uuid = 'e32f80df-6b59-46d9-b49e-4651aaf8a64e' + +[params] +type = 'Transmission' + +[params.mesh] +optimize = true +calculate_normals = true +calculate_tangents = false +calculate_texcoord = false diff --git a/assets/crayon/assets/crate.bmp b/assets/crayon/assets/crate.bmp new file mode 100755 index 000000000..73fe985a8 Binary files /dev/null and b/assets/crayon/assets/crate.bmp differ diff --git a/assets/crayon/assets/crate.bmp.meta.toml b/assets/crayon/assets/crate.bmp.meta.toml new file mode 100644 index 000000000..1465dd966 --- /dev/null +++ b/assets/crayon/assets/crate.bmp.meta.toml @@ -0,0 +1,15 @@ +name = 'crate.bmp' +uuid = '31ae371a-0b79-4eb2-a2bb-0bd527937e2d' + +[[resources]] +type = 'Texture' +name = 'crate.bmp' +uuid = '2943b938-6a27-4730-a507-02a904f384d5' + +[params] +type = 'Texture' +mipmap = true +alpha_premultiply = false +wrap = 'Clamp' +filter = 'Linear' +compression = 'HighQuality' diff --git a/assets/crayon/assets/music.mp3 b/assets/crayon/assets/music.mp3 new file mode 100644 index 000000000..50a8e0b39 Binary files /dev/null and b/assets/crayon/assets/music.mp3 differ diff --git a/assets/crayon/assets/music.mp3.meta.toml b/assets/crayon/assets/music.mp3.meta.toml new file mode 100644 index 000000000..90cdf0bc6 --- /dev/null +++ b/assets/crayon/assets/music.mp3.meta.toml @@ -0,0 +1,11 @@ +name = 'music.mp3' +uuid = '6d4cf11e-a3f2-4e6f-8d1f-117c00a2622f' + +[[resources]] +type = 'AudioClip' +name = 'music.mp3' +uuid = '4572a4b5-c9f1-46bd-8451-7f6fa535fae6' + +[params] +type = 'Audio' +compression = 'HighQuality' diff --git a/assets/crayon/assets/sfx.ogg b/assets/crayon/assets/sfx.ogg new file mode 100644 index 000000000..4ffaee5a0 Binary files /dev/null and b/assets/crayon/assets/sfx.ogg differ diff --git a/assets/crayon/assets/sfx.ogg.meta.toml b/assets/crayon/assets/sfx.ogg.meta.toml new file mode 100644 index 000000000..6d946102a --- /dev/null +++ b/assets/crayon/assets/sfx.ogg.meta.toml @@ -0,0 +1,11 @@ +name = 'sound_efx.ogg' +uuid = 'c8c0425f-79ed-41bc-baf1-2c8b6df6b0e9' + +[[resources]] +type = 'AudioClip' +name = 'sound_efx.ogg' +uuid = '427eb273-e774-46b8-b701-0ace1b7652ee' + +[params] +type = 'Audio' +compression = 'HighQuality' diff --git a/assets/crayon/resources/26AA066C59834DA8884C4AC7BF9C49F6 b/assets/crayon/resources/26AA066C59834DA8884C4AC7BF9C49F6 new file mode 100644 index 000000000..0db6cf052 Binary files /dev/null and b/assets/crayon/resources/26AA066C59834DA8884C4AC7BF9C49F6 differ diff --git a/assets/crayon/resources/2943B9386A274730A50702A904F384D5 b/assets/crayon/resources/2943B9386A274730A50702A904F384D5 new file mode 100644 index 000000000..995d9a799 Binary files /dev/null and b/assets/crayon/resources/2943B9386A274730A50702A904F384D5 differ diff --git a/assets/crayon/resources/427EB273E77446B8B7010ACE1B7652EE b/assets/crayon/resources/427EB273E77446B8B7010ACE1B7652EE new file mode 100644 index 000000000..9dc3a1880 Binary files /dev/null and b/assets/crayon/resources/427EB273E77446B8B7010ACE1B7652EE differ diff --git a/assets/crayon/resources/4572A4B5C9F146BD84517F6FA535FAE6 b/assets/crayon/resources/4572A4B5C9F146BD84517F6FA535FAE6 new file mode 100644 index 000000000..cd0549e7b Binary files /dev/null and b/assets/crayon/resources/4572A4B5C9F146BD84517F6FA535FAE6 differ diff --git a/assets/crayon/resources/64CD2E4E19494718AD79BEC5F4CD5600 b/assets/crayon/resources/64CD2E4E19494718AD79BEC5F4CD5600 new file mode 100644 index 000000000..88582a1d2 Binary files /dev/null and b/assets/crayon/resources/64CD2E4E19494718AD79BEC5F4CD5600 differ diff --git a/assets/crayon/resources/6F0C4447040F47C28F48BB8284DF5678 b/assets/crayon/resources/6F0C4447040F47C28F48BB8284DF5678 new file mode 100644 index 000000000..9e9be7434 Binary files /dev/null and b/assets/crayon/resources/6F0C4447040F47C28F48BB8284DF5678 differ diff --git a/assets/crayon/resources/D0CB0AC2AC6949C998CBF5841751B3FD b/assets/crayon/resources/D0CB0AC2AC6949C998CBF5841751B3FD new file mode 100644 index 000000000..8c70ec97e Binary files /dev/null and b/assets/crayon/resources/D0CB0AC2AC6949C998CBF5841751B3FD differ diff --git a/assets/crayon/resources/E32F80DF6B5946D9B49E4651AAF8A64E b/assets/crayon/resources/E32F80DF6B5946D9B49E4651AAF8A64E new file mode 100644 index 000000000..b9f964517 Binary files /dev/null and b/assets/crayon/resources/E32F80DF6B5946D9B49E4651AAF8A64E differ diff --git a/assets/crayon/resources/MANIFEST b/assets/crayon/resources/MANIFEST new file mode 100644 index 000000000..91573c42b Binary files /dev/null and b/assets/crayon/resources/MANIFEST differ diff --git a/assets/crayon/workspace.toml b/assets/crayon/workspace.toml new file mode 100644 index 000000000..b1ac920d8 --- /dev/null +++ b/assets/crayon/workspace.toml @@ -0,0 +1,40 @@ +[assets] +source = 'assets' +destination = 'resources' + +[[assets.importers]] +type = 'Texture' +extensions = [ + '.png', + '.jpg', + '.jpeg', + '.bmp', + '.tga', + '.psd', # Yes, we do supports .PSD files. +] + +[[assets.importers]] +type = 'Transmission' +extensions = [ + '.obj', + '.blend', + '.fbx', + '.gltf', + '.dae', + '.3ds', +] + +[[assets.importers]] +type = 'Audio' +extensions = [ + '.mp3', + '.wav', + '.ogg', +] + +[[assets.importers]] +type = 'Bytes' +extensions = [ + '.ttf', + '.otf', +] \ No newline at end of file diff --git a/assets/fonts/NotoSans/NotoSans-BoldItalic.ttf.meta.toml b/assets/fonts/NotoSans/NotoSans-BoldItalic.ttf.meta.toml new file mode 100644 index 000000000..0356e0bd0 --- /dev/null +++ b/assets/fonts/NotoSans/NotoSans-BoldItalic.ttf.meta.toml @@ -0,0 +1,10 @@ +name = 'fonts/NotoSans/NotoSans-BoldItalic.ttf' +uuid = 'b0a1740a-bc68-48e5-9c26-cc17089f2675' + +[[resources]] +type = 'Bytes' +name = 'fonts/NotoSans/NotoSans-BoldItalic.ttf' +uuid = '3b904684-995f-46e9-a6f4-567f284b82f3' + +[params] +type = 'Bytes' diff --git a/assets/fonts/NotoSans/NotoSans-Italic.ttf.meta.toml b/assets/fonts/NotoSans/NotoSans-Italic.ttf.meta.toml new file mode 100644 index 000000000..cd651a01c --- /dev/null +++ b/assets/fonts/NotoSans/NotoSans-Italic.ttf.meta.toml @@ -0,0 +1,10 @@ +name = 'fonts/NotoSans/NotoSans-Italic.ttf' +uuid = 'd17c06ff-d2d5-443e-865a-a26bde79c4bf' + +[[resources]] +type = 'Bytes' +name = 'fonts/NotoSans/NotoSans-Italic.ttf' +uuid = '8f9795bb-5c7d-4215-b38d-65ea3d784813' + +[params] +type = 'Bytes' diff --git a/assets/images/cornell_box.obj b/assets/images/cornell_box.obj new file mode 100644 index 000000000..fb00a8ddd --- /dev/null +++ b/assets/images/cornell_box.obj @@ -0,0 +1,118 @@ +# The original Cornell Box in OBJ format. +# Note that the real box is not a perfect cube, so +# the faces are imperfect in this data set. +# +# Created by Guedis Cardenas and Morgan McGuire at Williams College, 2011 +# Released into the Public Domain. +# +# http://graphics.cs.williams.edu/data +# http://www.graphics.cornell.edu/online/box/data.html +# + +## Object floor +v -1.01 0.00 0.99 +v 1.00 0.00 0.99 +v 1.00 0.00 -1.04 +v -0.99 0.00 -1.04 + +g floor +usemtl floor +f -4 -3 -2 -1 + +## Object backwall +v -0.99 0.00 -1.04 +v 1.00 0.00 -1.04 +v 1.00 1.99 -1.04 +v -1.02 1.99 -1.04 + +## Object shortBox +g shortBox +usemtl shortBox + +# Top Face +v 0.53 0.60 0.75 +v 0.70 0.60 0.17 +v 0.13 0.60 0.00 +v -0.05 0.60 0.57 +f -4 -3 -2 -1 + +# Left Face +v -0.05 0.00 0.57 +v -0.05 0.60 0.57 +v 0.13 0.60 0.00 +v 0.13 0.00 0.00 +f -4 -3 -2 -1 + +# Front Face +v 0.53 0.00 0.75 +v 0.53 0.60 0.75 +v -0.05 0.60 0.57 +v -0.05 0.00 0.57 +f -4 -3 -2 -1 + +# Right Face +v 0.70 0.00 0.17 +v 0.70 0.60 0.17 +v 0.53 0.60 0.75 +v 0.53 0.00 0.75 +f -4 -3 -2 -1 + +# Back Face +v 0.13 0.00 0.00 +v 0.13 0.60 0.00 +v 0.70 0.60 0.17 +v 0.70 0.00 0.17 +f -4 -3 -2 -1 + +# Bottom Face +v 0.53 0.00 0.75 +v 0.70 0.00 0.17 +v 0.13 0.00 0.00 +v -0.05 0.00 0.57 +f -12 -11 -10 -9 + +## Object tallBox +g tallBox +usemtl tallBox + +# Top Face +v -0.53 1.20 0.09 +v 0.04 1.20 -0.09 +v -0.14 1.20 -0.67 +v -0.71 1.20 -0.49 +f -4 -3 -2 -1 + +# Left Face +v -0.53 0.00 0.09 +v -0.53 1.20 0.09 +v -0.71 1.20 -0.49 +v -0.71 0.00 -0.49 +f -4 -3 -2 -1 + +# Back Face +v -0.71 0.00 -0.49 +v -0.71 1.20 -0.49 +v -0.14 1.20 -0.67 +v -0.14 0.00 -0.67 +f -4 -3 -2 -1 + +# Right Face +v -0.14 0.00 -0.67 +v -0.14 1.20 -0.67 +v 0.04 1.20 -0.09 +v 0.04 0.00 -0.09 +f -4 -3 -2 -1 + +# Front Face +v 0.04 0.00 -0.09 +v 0.04 1.20 -0.09 +v -0.53 1.20 0.09 +v -0.53 0.00 0.09 +f -4 -3 -2 -1 + +# Bottom Face +v -0.53 0.00 0.09 +v 0.04 0.00 -0.09 +v -0.14 0.00 -0.67 +v -0.71 0.00 -0.49 +f -8 -7 -6 -5 \ No newline at end of file diff --git a/assets/images/cornell_box.obj.meta.toml b/assets/images/cornell_box.obj.meta.toml new file mode 100644 index 000000000..69ad076d8 --- /dev/null +++ b/assets/images/cornell_box.obj.meta.toml @@ -0,0 +1,31 @@ +name = 'cornell_box.obj' +uuid = '13b56f8c-7724-4313-a5ff-d870db23b243' + +[[resources]] +type = 'Prefab' +name = 'cornell_box.obj' +uuid = '64cd2e4e-1949-4718-ad79-bec5f4cd5600' + +[[resources]] +type = 'Mesh' +name = 'floor' +uuid = '6f0c4447-040f-47c2-8f48-bb8284df5678' + +[[resources]] +type = 'Mesh' +name = 'shortBox' +uuid = 'd0cb0ac2-ac69-49c9-98cb-f5841751b3fd' + +[[resources]] +type = 'Mesh' +name = 'tallBox' +uuid = 'e32f80df-6b59-46d9-b49e-4651aaf8a64e' + +[params] +type = 'Transmission' + +[params.mesh] +optimize = true +calculate_normals = true +calculate_tangents = false +calculate_texcoord = false diff --git a/assets/images/crate.bmp b/assets/images/crate.bmp new file mode 100755 index 000000000..73fe985a8 Binary files /dev/null and b/assets/images/crate.bmp differ diff --git a/assets/images/rust.png.meta.toml b/assets/images/rust.png.meta.toml new file mode 100644 index 000000000..33a12eaa7 --- /dev/null +++ b/assets/images/rust.png.meta.toml @@ -0,0 +1,6 @@ +name = 'rust.png' +uuid = '6723cbed-c40c-42bf-ac33-93e9088322c7' +resources = [] + +[params] +type = 'Bytes' diff --git a/assets/images/rust_hover.png.meta.toml b/assets/images/rust_hover.png.meta.toml new file mode 100644 index 000000000..e49c4afc9 --- /dev/null +++ b/assets/images/rust_hover.png.meta.toml @@ -0,0 +1,6 @@ +name = 'rust_hover.png' +uuid = '24ec0d61-9ee5-4f9c-8b74-acd2f09ae404' +resources = [] + +[params] +type = 'Bytes' diff --git a/assets/images/rust_press.png.meta.toml b/assets/images/rust_press.png.meta.toml new file mode 100644 index 000000000..b7bc0a14b --- /dev/null +++ b/assets/images/rust_press.png.meta.toml @@ -0,0 +1,6 @@ +name = 'images/rust_press.png' +uuid = 'e1674a5f-a55d-44a7-951e-ebfd827b267f' +resources = [] + +[params] +type = 'Bytes' diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..3a8c60ae8 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,40 @@ +# Starter pipeline +# Start with a minimal pipeline that you can customize to build and deploy your code. +# Add steps that build, run tests, deploy, and more: +# https://aka.ms/yaml + +trigger: +- master + +pool: + vmImage: 'Ubuntu-16.04' + +steps: +- template: ci/azure-install-rust.yml +- template: ci/azure-install-node.yml +- script: which cargo + displayName: 'Run a one-line script' +- task: 1ESLighthouseEng.PipelineArtifactCaching.RestoreCacheV1.RestoreCache@1 + inputs: + keyfile: '**/package-lock.json, !**/node_modules/**/package-lock.json, Cargo.lock, Cargo.lock, Cargo.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules, target/debug/.fingerprint, target/debug/build, target/debug/deps, /usr/local/cargo/registry' + vstsFeed: '$(ArtifactFeed)' +- script: cargo install wasm-bindgen-cli +- script: cargo install xargo +- script: cd backends/conrod_crayon/tools/wasm-template && npm install +- task: 1ESLighthouseEng.PipelineArtifactCaching.SaveCacheV1.SaveCache@1 + inputs: + keyfile: '**/package-lock.json, !**/node_modules/**/package-lock.json, Cargo.lock, Cargo.lock, Cargo.lock' + targetfolder: '**/node_modules, !**/node_modules/**/node_modules, target/debug/.fingerprint, target/debug/build, target/debug/deps, /usr/local/cargo/registry' + vstsFeed: '$(ArtifactFeed)' +- script: cd backends/conrod_crayon/tools/wasm-template && npm ls | grep webpack + displayName: webpack version +- script: backends/conrod_crayon/tools/wasm-template/run.sh crayon +- task: AzureRmWebAppDeployment@3 + inputs: + azureSubscription: 'Free Trial' + WebAppName: 'conrod' + Package: '$(System.DefaultWorkingDirectory)/backends/conrod_crayon/tools/wasm-template' + GenerateWebConfig: true + WebConfigParameters: '-Handler iisnode -NodeStartFile app.js -appType node' + diff --git a/backends/conrod_crayon/Cargo.toml b/backends/conrod_crayon/Cargo.toml new file mode 100644 index 000000000..7aec650f6 --- /dev/null +++ b/backends/conrod_crayon/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "conrod_crayon" +version = "0.65.0" +authors = ["Mitchell Nordine "] +keywords = ["ui", "widgets", "gui", "interface", "graphics"] +description = "An easy-to-use, 100% Rust, extensible 2D GUI library." +license = "MIT OR Apache-2.0" +readme = "../../README.md" +repository = "https://github.com/pistondevelopers/conrod.git" +homepage = "https://github.com/pistondevelopers/conrod" +documentation = "https://docs.rs/conrod" +categories = ["gui"] + +[lib] +name = "conrod_crayon" +path = "./src/lib.rs" + +[dependencies] +conrod_core = { path = "../../conrod_core", version = "0.65" } +crayon = { git = "https://github.com/alanpoon/crayon.git", branch="textedit"} +conrod_example_shared = { path = "../conrod_example_shared", version = "0.65" } +serde_json = "1.0" +crayon_bytes = { git = "https://github.com/alanpoon/crayon.git", branch="textedit"} +cgmath = "0.16.1" +instant = {git="https://github.com/alanpoon/instant.git", branch="window_performance_stable", version="0.1.1", features = [ "wasm-bindgen","now" ]} +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2.47" + diff --git a/backends/conrod_crayon/examples/crayon.rs b/backends/conrod_crayon/examples/crayon.rs new file mode 100644 index 000000000..225bdee92 --- /dev/null +++ b/backends/conrod_crayon/examples/crayon.rs @@ -0,0 +1,144 @@ +//Resources to be build by crayon-cli tool +extern crate crayon; +extern crate conrod_crayon; +extern crate conrod_example_shared; +#[macro_use] extern crate conrod_core; +extern crate crayon_bytes; + +use crayon::prelude::*; +use crayon_bytes::prelude::*; +use crayon::window::device_pixel_ratio; +use conrod_crayon::Renderer; +use conrod_example_shared::{WIN_W, WIN_H}; +use std::collections::HashMap; +use conrod_core::{color,Colorable, widget, Widget,Positionable,event::{Input},Sizeable,Labelable}; +use conrod_core::text::{Font,FontCollection}; +#[derive(Debug, Clone, Copy)] +struct WindowResources { + b: BytesHandle, +} + +impl WindowResources { + pub fn new() -> CrResult { + crayon_bytes::setup()?; + Ok(WindowResources { + b: crayon_bytes::create_bytes_from("res:Oswald-Heavy.ttf")?, + }) + } +} +impl LatchProbe for WindowResources { + fn is_set(&self) -> bool { + crayon_bytes::state(self.b) != ResourceState::NotReady + } +} +widget_ids!(struct Ids { text,canvas,scrollbar }); +struct Window { + text:String, + renderer: Renderer, + app: conrod_example_shared::DemoApp, + ui: conrod_core::Ui, + ids: Ids, + image_map: conrod_core::image::Map, + batch: CommandBuffer, + time: f32, + resources: WindowResources +} + +impl Window { + pub fn build(resources: &WindowResources) -> CrResult { + + let mut ui = conrod_core::UiBuilder::new([WIN_W as f64, WIN_H as f64]) + .build(); + let ids = Ids::new(ui.widget_id_generator()); + let mut image_map: conrod_core::image::Map = conrod_core::image::Map::new(); + let rust_logo = image_map.insert(load_crate_logo()); + let app = conrod_example_shared::DemoApp::new(rust_logo); + let dpi_factor = device_pixel_ratio(); + let renderer = conrod_crayon::Renderer::new((WIN_W as f64,WIN_H as f64), dpi_factor as f64); + let f = ui.fonts.insert(load_bold(resources.b)); + ui.theme.font_id = Some(f); + let mut demo_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. \ + Mauris aliquet porttitor tellus vel euismod. Integer lobortis volutpat bibendum. Nulla \ + finibus odio nec elit condimentum, rhoncus fermentum purus lacinia. Interdum et malesuada \ + fames ac ante ipsum primis in faucibus. Cras rhoncus nisi nec dolor bibendum pellentesque. \ + Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. \ + Quisque commodo nibh hendrerit nunc sollicitudin sodales. Cras vitae tempus ipsum. Nam \ + magna est, efficitur suscipit dolor eu, consectetur consectetur urna.".to_owned(); + Ok(Window { + app:app, + text: demo_text, + ui:ui, + ids:ids, + image_map:image_map, + renderer:renderer, + batch: CommandBuffer::new(), + time: 0.0, + resources: *resources + }) + } +} + +impl Drop for Window { + fn drop(&mut self) { + + } +} + +impl LifecycleListener for Window { + fn on_update(&mut self) -> CrResult<()> { + conrod_crayon::events::convert_event(&mut self.ui); + { + let mut ui = &mut self.ui.set_widgets(); + + const LOGO_SIDE: conrod_core::Scalar = 306.0; + + widget::Canvas::new() + .scroll_kids_vertically() + .color(color::BLUE) + .set(self.ids.canvas, ui); + + + for edit in widget::TextEdit::new(&self.text) + .color(color::WHITE) + .font_size(20) + .padded_w_of(self.ids.canvas, 20.0) + .mid_top_of(self.ids.canvas) + .center_justify() + .line_spacing(2.5) + .set(self.ids.text,ui){ + self.text = edit; + + } + + } + + let dpi_factor = device_pixel_ratio() as f64; + let primitives = self.ui.draw(); + let dims = (WIN_W as f64 * dpi_factor, WIN_H as f64 * dpi_factor); + self.renderer.fill(dims,dpi_factor as f64,primitives,&self.image_map); + self.renderer.draw(&mut self.batch,&self.image_map); + + Ok(()) + } +} +fn load_crate_logo() -> TextureHandle { + video::create_texture_from("res:crate.bmp").unwrap() +} +fn load_bold(handle:BytesHandle) ->Font{ + FontCollection::from_bytes(crayon_bytes::create_bytes(handle).unwrap()).unwrap().into_font().unwrap() +} +main!({ + #[cfg(not(target_arch = "wasm32"))] + let res = format!("file://{}/../../assets/crayon/resources/", env!("CARGO_MANIFEST_DIR").replace("\\","/")); + #[cfg(target_arch = "wasm32")] + let res = format!("http://localhost:8080/resources/"); + let mut params = Params::default(); + params.window.title = "CR: RenderTexture".into(); + params.window.size = (WIN_W as u32, WIN_H as u32).into(); + params.res.shortcuts.add("res:", res).unwrap(); + params.res.dirs.push("res:".into()); + crayon::application::setup(params,|| { + let resources = WindowResources::new()?; + Ok(Launcher::new(resources, |r| Window::build(r))) + }).unwrap(); +}); \ No newline at end of file diff --git a/backends/conrod_crayon/src/events.rs b/backends/conrod_crayon/src/events.rs new file mode 100644 index 000000000..db628f4e7 --- /dev/null +++ b/backends/conrod_crayon/src/events.rs @@ -0,0 +1,80 @@ +use conrod_core::event::Input; +use conrod_core::input::Button::Keyboard; +use conrod_core::input::Motion; +use conrod_core::Ui; +use crayon::prelude::*; +use instant::Instant; +pub fn convert_event(ui:&mut Ui)->Option{ + let mouse_presses = input::mouse_presses(); + let w = ui.win_w; + let h = ui.win_h; + let mut action_time = None; + for mp in mouse_presses.iter(){ + let e = match mp{ + crayon::input::mouse::MouseButton::Left => conrod_core::input::state::mouse::Button::Left, + crayon::input::mouse::MouseButton::Right => conrod_core::input::state::mouse::Button::Right, + crayon::input::mouse::MouseButton::Middle => conrod_core::input::state::mouse::Button::Middle, + crayon::input::mouse::MouseButton::Other(_j) => conrod_core::input::state::mouse::Button::Unknown + }; + ui.handle_event(Input::Press(conrod_core::input::Button::Mouse(e))); + if let None = action_time{ + action_time = Some(Instant::now()); + } + } + let mouse_releases = input::mouse_releases(); + for mp in mouse_releases.iter(){ + let e = match mp{ + crayon::input::mouse::MouseButton::Left => conrod_core::input::state::mouse::Button::Left, + crayon::input::mouse::MouseButton::Right => conrod_core::input::state::mouse::Button::Right, + crayon::input::mouse::MouseButton::Middle => conrod_core::input::state::mouse::Button::Middle, + crayon::input::mouse::MouseButton::Other(_j) => conrod_core::input::state::mouse::Button::Unknown + }; + ui.handle_event(Input::Release(conrod_core::input::Button::Mouse(e))); + if let None = action_time{ + action_time = Some(Instant::now()); + } + } + let key_presses = input::key_presses(); + for kp in key_presses.iter(){ + let e = key_convert(serde_json::to_string(kp).unwrap()); + let ee:conrod_core::input::keyboard::Key = serde_json::from_str(&e).unwrap(); + ui.handle_event(Input::Press(Keyboard(ee))); + if let None = action_time{ + action_time = Some(Instant::now()); + } + } + let key_releases = input::key_releases(); + for kp in key_releases.iter(){ + let e = key_convert(serde_json::to_string(kp).unwrap()); + let ee:conrod_core::input::keyboard::Key = serde_json::from_str(&e).unwrap(); + ui.handle_event(Input::Release(Keyboard(ee))); + if let None = action_time{ + action_time = Some(Instant::now()); + } + } + + let j = input::mouse_position(); + ui.handle_event(Input::Motion(Motion::MouseCursor{x:(j.x as f64)-w/2.0,y:(j.y as f64)-h/2.0})); + let j = input::mouse_scroll(); + if j.x > 0.0 || j.y >0.0{ + ui.handle_event(Input::Motion(Motion::Scroll{x:(j.x as f64)-w/2.0,y:(j.y as f64)-h/2.0})); + if let None = action_time{ + action_time = Some(Instant::now()); + } + } + + for k in input::chars(){ + if k != '\u{8}'{ + ui.handle_event(Input::Text(k.to_string())); + } + if let None = action_time{ + action_time = Some(Instant::now()); + } + } + action_time +} + +pub fn key_convert(j:String)->String{ + j.replace("Key","D").replace("Control","Ctrl").replace("LBracket","LeftBracket").replace("RBracket","RightBracket") + .replace("Subtract","Minus").replace("Add","Plus").replace("Back","Backspace").replace("Backspaceslash","Backslash") +} diff --git a/backends/conrod_crayon/src/lib.rs b/backends/conrod_crayon/src/lib.rs new file mode 100644 index 000000000..1b48f262d --- /dev/null +++ b/backends/conrod_crayon/src/lib.rs @@ -0,0 +1,569 @@ +extern crate crayon; +extern crate conrod_core; +extern crate serde_json; +extern crate cgmath; +extern crate instant; +use crayon::impl_vertex; +use crayon::prelude::*; +use crayon::errors::Result; +use crayon::video::assets::texture::*; +pub mod events; +use conrod_core::{ + Rect, + Scalar, + color, + image, + render, + text, +}; + +impl_vertex! { + Vertex { + pos => [Position; Float; 2; false], + uv =>[Texcoord0; Float; 2; false], + color =>[Color0; Float; 4; true], + mode =>[Weight; Float; 1; false], + } +} + +/// Draw text from the text cache texture `tex` in the fragment shader. +pub const MODE_TEXT: f32 = 0.0; +/// Draw an image from the texture at `tex` in the fragment shader. +pub const MODE_IMAGE: f32 = 1.0; +/// Ignore `tex` and draw simple, colored 2D geometry. +pub const MODE_GEOMETRY: f32 = 2.0; +/// A `Command` describing a step in the drawing process. +#[derive(Clone, Debug)] +pub enum Command<'a> { + /// Draw to the target. + Draw(DrawE<'a>), + /// Update the scizzor within the `glium::DrawParameters`. + Scizzor(SurfaceScissor), +} +enum PreparedCommand { + Image(image::Id, std::ops::Range), + Plain(std::ops::Range), + Scizzor(SurfaceScissor), +} +/// A rusttype `GlyphCache` along with a `glium::texture::Texture2d` for caching text on the `GPU`. +pub struct GlyphCache { + cache: text::GlyphCache<'static>, + texture: TextureHandle, +} + +impl GlyphCache { + /// Construct a **GlyphCache** with the given texture dimensions. + /// + /// When calling `GlyphCache::new`, the `get_framebuffer_dimensions` method is used to produce + /// the width and height. However, often creating a texture the size of the screen might not be + /// large enough to cache the necessary text for an application. The following constant + /// multiplier is used to ensure plenty of room in the cache. + pub fn with_dimensions( + width: u32, + height: u32, + ) -> Result + { + // First, the rusttype `Cache` which performs the logic for rendering and laying out glyphs + // in the cache. + let cache = rusttype_glyph_cache(width, height); + + // Now the texture to which glyphs will be rendered. + let texture = glyph_cache_texture(width, height)?; + + Ok(GlyphCache { + cache: cache, + texture: texture, + }) + } + + /// Construct a `GlyphCache` with a size equal to the given `Display`'s current framebuffer + /// dimensions. + pub fn new(dim:(f64,f64)) -> Result + { + Self::with_dimensions(dim.0 as u32, dim.1 as u32) + } + + /// The texture used to cache the glyphs on the GPU. + pub fn texture(&self) -> &TextureHandle { + &self.texture + } +} + +pub struct Renderer { + glyph_cache: GlyphCache, + vertices: Vec, + shader: ShaderHandle, + surface: SurfaceHandle, + //rendered_texture:RenderTextureHandle, + commands: Vec, +} +/// A `Command` for drawing to the target. +/// +/// Each variant describes how to draw the contents of the vertex buffer. +#[derive(Clone, Debug)] +pub enum DrawE<'a> { + /// A range of vertices representing triangles textured with the image in the + /// image_map at the given `widget::Id`. + Image(image::Id, &'a [Vertex]), + /// A range of vertices representing plain triangles. + Plain(&'a [Vertex]), + Test, +} + +pub struct Commands<'a> { + commands: std::slice::Iter<'a, PreparedCommand>, + vertices: &'a [Vertex], +} +impl Renderer{ + pub fn new(dim:(f64,f64),dpi_factor: f64)->Self{ + let attributes = AttributeLayoutBuilder::new() + .with(Attribute::Position, 2) + .with(Attribute::Texcoord0, 2) + .with(Attribute::Color0, 4) + .with(Attribute::Weight,1) + .finish(); + let uniforms = UniformVariableLayout::build() + .with("tex", UniformVariableType::Texture) + .finish(); + let mut params = ShaderParams::default(); + + params.state.color_blend = Some((crayon::video::assets::shader::Equation::Add, + crayon::video::assets::shader::BlendFactor::Value(crayon::video::assets::shader::BlendValue::SourceAlpha), + crayon::video::assets::shader::BlendFactor::OneMinusValue(crayon::video::assets::shader::BlendValue::SourceAlpha))); + + params.attributes = attributes; + params.uniforms = uniforms; + //looking for Position + let vs = include_str!("shaders/conrod.vs").to_owned(); + let fs = include_str!("shaders/conrod.fs").to_owned(); + let shader = video::create_shader(params.clone(), vs, fs).unwrap(); + + let mut params = SurfaceParams::default(); + params.set_clear(None, None, None); + let vert:Vec = Vec::new(); + let commands:Vec = Vec::new(); + let surface = video::create_surface(params).unwrap(); + + Renderer{ + glyph_cache:GlyphCache::new((dim.0 * dpi_factor,dim.1 * dpi_factor)).unwrap(), + vertices: vert, + shader:shader, + surface:surface, + commands: commands + } + } + + pub fn commands(&self) -> Commands { + let Renderer { ref commands, ref vertices, .. } = *self; + Commands { + commands: commands.iter(), + vertices: vertices, + } + } + pub fn fill

(&mut self,dims: (f64, f64),dpi_factor: f64,mut primitives: P, image_map:&conrod_core::image::Map )where P: render::PrimitiveWalker{ + let (screen_w, screen_h) = dims; + let half_win_w = screen_w / 2.0; + let half_win_h = screen_h / 2.0; + let Renderer { ref mut vertices,shader,surface,ref mut commands, ref mut glyph_cache,..} = *self; + commands.clear(); + vertices.clear(); + let mut current_scissor =SurfaceScissor::Enable{ + position: Vector2{x:0,y:0}, + size: Vector2{x:screen_w as u32,y:screen_h as u32} + }; + enum State { + Image { image_id: image::Id, start: usize }, + Plain { start: usize }, + } + let mut current_state = State::Plain { start: 0 }; + macro_rules! switch_to_plain_state { + () => { + match current_state { + State::Plain { .. } => (), + State::Image { image_id, start } => { + commands.push(PreparedCommand::Image(image_id, start..vertices.len())); + current_state = State::Plain { start: vertices.len() }; + }, + } + }; + } + let rect_to_crayon_rect = |rect: Rect| { + let (w, h) = rect.w_h(); + let left = (rect.left() * dpi_factor + half_win_w) as i32; + let bottom = (rect.bottom() * dpi_factor + half_win_h) as i32; + let width = (w * dpi_factor) as u32; + let height = (h * dpi_factor) as u32; + SurfaceScissor::Enable{ + position: Vector2{x:std::cmp::max(left, 0),y:std::cmp::max(bottom, 0)}, + size: Vector2{x:std::cmp::min(width, screen_w as u32),y: std::cmp::min(height, screen_h as u32)} + } + }; + + let vx = |x: f64| (x * dpi_factor / half_win_w) as f32; + let vy = |y: f64| (y * dpi_factor / half_win_h) as f32; + + while let Some(primitive) = primitives.next_primitive() { + let render::Primitive { kind, scizzor, rect, .. } = primitive; + + let new_scissor = rect_to_crayon_rect(scizzor); + if new_scissor != current_scissor { + // Finish the current command. + match current_state { + State::Plain { start } => + commands.push(PreparedCommand::Plain(start..vertices.len())), + State::Image { image_id, start } => + commands.push(PreparedCommand::Image(image_id, start..vertices.len())), + } + + // Update the scizzor and produce a command. + current_scissor = new_scissor; + commands.push(PreparedCommand::Scizzor(current_scissor)); + + // Set the state back to plain drawing. + current_state = State::Plain { start: vertices.len() }; + } + + match kind { + + render::PrimitiveKind::Rectangle { color } => { + switch_to_plain_state!(); + let color = gamma_srgb_to_linear(color.to_fsa()); + let (l, r, b, t) = rect.l_r_b_t(); + let v = |x, y| { + Vertex::new([vx(x),vy(y)],[0.0,0.0],color,MODE_GEOMETRY) + }; + + let mut push_v = |x, y| vertices.push(v(x, y)); + + // Bottom left triangle. + push_v(l, t); + push_v(r, b); + push_v(l, b); + + // Top right triangle. + push_v(l, t); + push_v(r, b); + push_v(r, t); + + }, + + render::PrimitiveKind::TrianglesSingleColor { color, triangles } => { + if triangles.is_empty() { + continue; + } + + switch_to_plain_state!(); + + let color = gamma_srgb_to_linear(color.into()); + + let v = |p: [Scalar; 2]| { + Vertex::new([vx(p[0]), vy(p[1])],[0.0,0.0],color,MODE_GEOMETRY) + }; + + for triangle in triangles { + vertices.push(v(triangle[0])); + vertices.push(v(triangle[1])); + vertices.push(v(triangle[2])); + } + }, + + render::PrimitiveKind::TrianglesMultiColor { triangles } => { + if triangles.is_empty() { + continue; + } + + switch_to_plain_state!(); + + let v = |(p, c): ([Scalar; 2], color::Rgba)| { + Vertex::new([vx(p[0]), vy(p[1])],[0.0,0.0],gamma_srgb_to_linear(c.into()),MODE_GEOMETRY) + }; + + for triangle in triangles { + vertices.push(v(triangle[0])); + vertices.push(v(triangle[1])); + vertices.push(v(triangle[2])); + } + }, + render::PrimitiveKind::Text { color, text, font_id } => { + switch_to_plain_state!(); + + let positioned_glyphs = text.positioned_glyphs(dpi_factor as f32); + + let GlyphCache { ref mut cache, ref mut texture } = *glyph_cache; + + // Queue the glyphs to be cached. + for glyph in positioned_glyphs.iter() { + cache.queue_glyph(font_id.index(), glyph.clone()); + } + + // Cache the glyphs on the GPU. + cache.cache_queued(|rect, data| { + let width = rect.width(); + let height = rect.height(); + let lbwh = [rect.min.x,rect.min.y,rect.max.x,rect.max.y]; + let p1 = cgmath::Point2::new(lbwh[0],lbwh[1]); + let p2 = cgmath::Point2::new(lbwh[2],lbwh[3]); + let rect = crayon::math::aabb::Aabb2::new(p1,p2); + let mut new_data:Vec = vec![]; + for j in data.to_vec(){ + new_data.push(j); + for _z in 1..4{ + new_data.push(0); + } + } + video::update_texture(*texture,rect,&new_data).unwrap(); + + }).unwrap(); + + let color = gamma_srgb_to_linear(color.to_fsa()); + let cache_id = font_id.index(); + + let origin = text::rt::point(0.0, 0.0); + let to_gl_rect = |screen_rect: text::rt::Rect| text::rt::Rect { + min: origin + + (text::rt::vector(screen_rect.min.x as f32 / screen_w as f32 - 0.5, + 1.0 - screen_rect.min.y as f32 / screen_h as f32 - 0.5)) * 2.0, + max: origin + + (text::rt::vector(screen_rect.max.x as f32 / screen_w as f32 - 0.5, + 1.0 - screen_rect.max.y as f32 / screen_h as f32 - 0.5)) * 2.0 + }; + let mut l =0; + for g in positioned_glyphs { + + if let Ok(Some((uv_rect, screen_rect))) = cache.rect_for(cache_id, g) { + + let gl_rect = to_gl_rect(screen_rect); + + let v = |p:[f32;2],t:[f32;2]| {Vertex::new(p,t,color,MODE_TEXT)}; + let mut push_v = |p, t| vertices.push(v(p, t)); + push_v([gl_rect.min.x, gl_rect.max.y], [uv_rect.min.x, uv_rect.max.y]); + push_v([gl_rect.min.x, gl_rect.min.y], [uv_rect.min.x, uv_rect.min.y]); + push_v([gl_rect.max.x, gl_rect.min.y], [uv_rect.max.x, uv_rect.min.y]); + push_v([gl_rect.max.x, gl_rect.min.y], [uv_rect.max.x, uv_rect.min.y]); + push_v([gl_rect.max.x, gl_rect.max.y], [uv_rect.max.x, uv_rect.max.y]); + push_v([gl_rect.min.x, gl_rect.max.y], [uv_rect.min.x, uv_rect.max.y]); + l = l+1; + + } + } + + }, + render::PrimitiveKind::Image { image_id, color, source_rect } => { + // Switch to the `Image` state for this image if we're not in it already. + let new_image_id = image_id; + match current_state { + + // If we're already in the drawing mode for this image, we're done. + State::Image { image_id, .. } if image_id == new_image_id => (), + + // If we were in the `Plain` drawing state, switch to Image drawing state. + State::Plain { start } => { + commands.push(PreparedCommand::Plain(start..vertices.len())); + current_state = State::Image { + image_id: new_image_id, + start: vertices.len(), + }; + }, + + // If we were drawing a different image, switch state to draw *this* image. + State::Image { image_id, start } => { + commands.push(PreparedCommand::Image(image_id, start..vertices.len())); + current_state = State::Image { + image_id: new_image_id, + start: vertices.len(), + }; + }, + } + + let color = color.unwrap_or(color::WHITE).to_fsa(); + + if let Some(&image) = image_map.get(&image_id) { + let mut image_w:f64 = 100.0; + let mut image_h:f64 = 100.0; + if let Some(image_param) = video::texture(image){ + image_w = image_param.dimensions.x as f64; + image_h = image_param.dimensions.y as f64; + } + + // Get the sides of the source rectangle as uv coordinates. + // + // Texture coordinates range: + // - left to right: 0.0 to 1.0 + // - bottom to top: 0.0 to 1.0 + let (uv_l, uv_r, uv_b, uv_t) = match source_rect { + Some(src_rect) => { + let (l, r, b, t) = src_rect.l_r_b_t(); + ((l / image_w) as f32, + (r / image_w) as f32, + (b / image_h) as f32, + (t / image_h) as f32) + }, + + None => (0.0, 1.0 , 0.0, 1.0), + }; + + let v = |x, y, t| { + // Convert from conrod Scalar range to GL range -1.0 to 1.0. + let x = (x * dpi_factor as Scalar / half_win_w) as f32; + let y = (y * dpi_factor as Scalar / half_win_h) as f32; + Vertex::new([x, y],t,color,MODE_IMAGE) + }; + + let mut push_v = |x, y, t| vertices.push(v(x, y, t)); + + let (l, r, b, t) = rect.l_r_b_t(); + + // Bottom left triangle. + push_v(l, t, [uv_l, uv_t]); + push_v(r, b, [uv_r, uv_b]); + push_v(l, b, [uv_l, uv_b]); + + // Top right triangle. + push_v(l, t, [uv_l, uv_t]); + push_v(r, b, [uv_r, uv_b]); + push_v(r, t, [uv_r, uv_t]); + } + }, + render::PrimitiveKind::Other(_) => (), + _=>{} + } + } + // Enter the final command. + + match current_state { + State::Plain { start } => + commands.push(PreparedCommand::Plain(start..vertices.len())), + State::Image { image_id, start } => + commands.push(PreparedCommand::Image(image_id, start..vertices.len())), + } + + } + pub fn draw(&self,batch: &mut CommandBuffer,image_map:&conrod_core::image::Map){ + const NUM_VERTICES_IN_TRIANGLE: usize = 3; + let uniform =self.glyph_cache.texture(); //can use rendertexture + + for command in self.commands() { + match command { + Command::Scizzor(scizzor) => { + batch.update_scissor(scizzor) + }, + + // Draw to the target with the given `draw` command. + Command::Draw(draw) => match draw { + DrawE::Plain(slice) => if slice.len() >= NUM_VERTICES_IN_TRIANGLE { + let mut idxes:Vec = vec![]; + for i in 0..slice.len(){ + idxes.push(i as u16); + } + + let mut params = MeshParams::default(); + params.num_verts = slice.len(); + params.num_idxes = slice.len(); + params.primitive = MeshPrimitive::Triangles; + params.layout = Vertex::layout(); + let data = MeshData { + vptr: Vertex::encode(&slice[..]).into(), + iptr: IndexFormat::encode(&idxes).into(), + }; + let mesh = video::create_mesh(params.clone(), Some(data)).unwrap(); + let mut dc = Draw::new(self.shader, mesh); + dc.set_uniform_variable("tex", *uniform); + batch.draw(dc); + batch.submit(self.surface).unwrap(); + + }, + // Draw an image whose texture data lies within the `image_map` at the + // given `id`. + // + // Only submit the vertices if there is enough for at least one triangle. + DrawE::Image(image_id, slice) => if slice.len() >= NUM_VERTICES_IN_TRIANGLE { + if let Some(&image) = image_map.get(&image_id) { + + let mut idxes:Vec = vec![]; + for i in 0..slice.len(){ + idxes.push(i as u16); + } + + let mut params = MeshParams::default(); + params.num_verts = slice.len(); + params.num_idxes = slice.len(); + params.primitive = MeshPrimitive::Triangles; + params.layout = Vertex::layout(); + let data = MeshData { + vptr: Vertex::encode(&slice[..]).into(), + iptr: IndexFormat::encode(&idxes).into(), + }; + + let mesh = video::create_mesh(params, Some(data)).unwrap(); + let mut dc = Draw::new(self.shader, mesh); + dc.set_uniform_variable("tex", image); + batch.draw(dc); + batch.submit(self.surface).unwrap() + } + }, + + _=>{ + + } + } + } + } + + + } +} + +fn gamma_srgb_to_linear(c: [f32; 4]) -> [f32; 4] { + fn component(f: f32) -> f32 { + // Taken from https://github.com/PistonDevelopers/graphics/src/color.rs#L42 + if f <= 0.04045 { + f / 12.92 + } else { + ((f + 0.055) / 1.055).powf(2.4) + } + } + [component(c[0]), component(c[1]), component(c[2]), c[3]] +} + +impl<'a> Iterator for Commands<'a> { + type Item = Command<'a>; + fn next(&mut self) -> Option { + let Commands { ref mut commands, ref vertices } = *self; + commands.next().map(|command| match *command { + PreparedCommand::Scizzor(scizzor) => Command::Scizzor(scizzor), + PreparedCommand::Plain(ref range) => + Command::Draw(DrawE::Plain(&vertices[range.clone()])), + PreparedCommand::Image(id, ref range) => + Command::Draw(DrawE::Image(id, &vertices[range.clone()])), + }) + } +} +// Creating the rusttype glyph cache used within a `GlyphCache`. +fn rusttype_glyph_cache(w: u32, h: u32) -> text::GlyphCache<'static> { + const SCALE_TOLERANCE: f32 = 0.1; + const POSITION_TOLERANCE: f32 = 0.1; + text::GlyphCache::builder() + .dimensions(w, h) + .scale_tolerance(SCALE_TOLERANCE) + .position_tolerance(POSITION_TOLERANCE) + .build() +} + +// Create the texture used within a `GlyphCache` of the given size. +fn glyph_cache_texture( + width: u32, + height: u32, +) -> Result +{ + // Determine the optimal texture format to use given the opengl version. + let mut params = TextureParams::default(); + params.format = TextureFormat::RGBA8; + params.hint = TextureHint::Stream; + params.dimensions = (width, height).into(); + let data_size = params.format.size(params.dimensions) as usize; + let bytes = vec![]; + let data = TextureData{ + bytes:bytes + }; + video::create_texture(params,data) +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/conrod.1.fs b/backends/conrod_crayon/src/shaders/conrod.1.fs new file mode 100644 index 000000000..d688d3ddd --- /dev/null +++ b/backends/conrod_crayon/src/shaders/conrod.1.fs @@ -0,0 +1,21 @@ +#version 100 +precision lowp float; +varying vec2 v_tex_coords; +varying vec4 v_color; +varying float mode; +uniform sampler2D tex; + +void main() { + // Text + if (mode < 0.1) { + gl_FragColor = v_color * vec4(1.0, 1.0, 1.0, texture2D(tex, v_tex_coords).r); + + // Image + } else if (mode < 1.1) { + gl_FragColor = texture2D(tex, v_tex_coords); + + // 2D Geometry + } else if (mode < 2.1) { + gl_FragColor = v_color; + } +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/conrod.1.vs b/backends/conrod_crayon/src/shaders/conrod.1.vs new file mode 100644 index 000000000..0df5f83db --- /dev/null +++ b/backends/conrod_crayon/src/shaders/conrod.1.vs @@ -0,0 +1,17 @@ +#version 100 +precision lowp float; + +attribute lowp vec2 Position; +attribute lowp vec2 Texcoord0; +attribute lowp vec4 Color0; +attribute lowp float Weight; + +varying vec2 v_tex_coords; +varying vec4 v_color; +varying float mode; +void main() { + gl_Position = vec4(Position, 0.0, 1.0); + v_tex_coords = Texcoord0; + v_color = Color0; + mode = Weight; +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/conrod.fs b/backends/conrod_crayon/src/shaders/conrod.fs new file mode 100644 index 000000000..dcfe0d916 --- /dev/null +++ b/backends/conrod_crayon/src/shaders/conrod.fs @@ -0,0 +1,25 @@ +#version 300 es +precision mediump float; +uniform sampler2D tex; + +in float v_mode; +in vec2 v_tex_coords; +in vec4 v_color; + +out vec4 f_color; + +void main() { + // Text + + if (v_mode < 0.1 ) { + f_color = v_color * vec4(1.0, 1.0, 1.0, texture(tex, v_tex_coords).r); + + // Image + } else if (v_mode < 1.1) { + f_color = texture(tex, v_tex_coords); + + // 2D Geometry + } else if (v_mode < 2.1){ + f_color = v_color; + } +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/conrod.vs b/backends/conrod_crayon/src/shaders/conrod.vs new file mode 100644 index 000000000..843834625 --- /dev/null +++ b/backends/conrod_crayon/src/shaders/conrod.vs @@ -0,0 +1,18 @@ +#version 300 es +precision mediump float; + +in vec2 Position; +in vec2 Texcoord0; +in vec4 Color0; +in float Weight; + +out vec4 v_color; +out vec2 v_tex_coords; +out float v_mode; + +void main() { + gl_Position = vec4(Position, 0.0, 1.0); + v_tex_coords = Texcoord0; + v_color = Color0; + v_mode = Weight; +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/render_target_p1.fs b/backends/conrod_crayon/src/shaders/render_target_p1.fs new file mode 100644 index 000000000..54dd3044c --- /dev/null +++ b/backends/conrod_crayon/src/shaders/render_target_p1.fs @@ -0,0 +1,8 @@ +#version 100 +precision lowp float; + +varying vec2 v_Color; + +void main() { + gl_FragColor = vec4(v_Color, 0.0, 1.0); +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/render_target_p1.vs b/backends/conrod_crayon/src/shaders/render_target_p1.vs new file mode 100644 index 000000000..0ed75bedf --- /dev/null +++ b/backends/conrod_crayon/src/shaders/render_target_p1.vs @@ -0,0 +1,10 @@ +#version 100 +precision lowp float; + +attribute vec2 Position; +varying vec2 v_Color; + +void main() { + gl_Position = vec4(Position, 0.0, 1.0); + v_Color = Position; +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/render_target_p2.fs b/backends/conrod_crayon/src/shaders/render_target_p2.fs new file mode 100644 index 000000000..e0e68fadb --- /dev/null +++ b/backends/conrod_crayon/src/shaders/render_target_p2.fs @@ -0,0 +1,12 @@ +#version 100 +precision lowp float; + +varying vec2 v_Texcoord; + +uniform sampler2D renderedTexture; +uniform float time; + +void main() { + vec2 offset = 0.025*vec2(sin(time+1024.0*v_Texcoord.x), cos(time+768.0*v_Texcoord.y)); + gl_FragColor = vec4(texture2D( renderedTexture, v_Texcoord + offset ).xyz, 1.0); +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/render_target_p2.vs b/backends/conrod_crayon/src/shaders/render_target_p2.vs new file mode 100644 index 000000000..f8d5d4991 --- /dev/null +++ b/backends/conrod_crayon/src/shaders/render_target_p2.vs @@ -0,0 +1,10 @@ +#version 100 +precision lowp float; + +attribute vec2 Position; +varying vec2 v_Texcoord; + +void main(){ + gl_Position = vec4(Position, 0.0, 1.0); + v_Texcoord = (Position + vec2(1.0, 1.0)) / 2.0; +} diff --git a/backends/conrod_crayon/src/shaders/texture.fs b/backends/conrod_crayon/src/shaders/texture.fs new file mode 100644 index 000000000..d478ceb80 --- /dev/null +++ b/backends/conrod_crayon/src/shaders/texture.fs @@ -0,0 +1,10 @@ +#version 100 +precision lowp float; + +varying vec2 v_Texcoord; + +uniform sampler2D renderedTexture; + +void main() { + gl_FragColor = vec4(texture2D( renderedTexture, v_Texcoord ).xyz, 1.0); +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/texture.vs b/backends/conrod_crayon/src/shaders/texture.vs new file mode 100644 index 000000000..f8d5d4991 --- /dev/null +++ b/backends/conrod_crayon/src/shaders/texture.vs @@ -0,0 +1,10 @@ +#version 100 +precision lowp float; + +attribute vec2 Position; +varying vec2 v_Texcoord; + +void main(){ + gl_Position = vec4(Position, 0.0, 1.0); + v_Texcoord = (Position + vec2(1.0, 1.0)) / 2.0; +} diff --git a/backends/conrod_crayon/src/shaders/triangle.fs b/backends/conrod_crayon/src/shaders/triangle.fs new file mode 100644 index 000000000..54dd3044c --- /dev/null +++ b/backends/conrod_crayon/src/shaders/triangle.fs @@ -0,0 +1,8 @@ +#version 100 +precision lowp float; + +varying vec2 v_Color; + +void main() { + gl_FragColor = vec4(v_Color, 0.0, 1.0); +} \ No newline at end of file diff --git a/backends/conrod_crayon/src/shaders/triangle.vs b/backends/conrod_crayon/src/shaders/triangle.vs new file mode 100644 index 000000000..396641e8a --- /dev/null +++ b/backends/conrod_crayon/src/shaders/triangle.vs @@ -0,0 +1,10 @@ +#version 100 +precision lowp float; + +attribute vec2 Position; +varying vec2 v_Color; + +void main() { + gl_Position = vec4(Position, 0.0, 1.0); + v_Color = vec2(0.0,0.0); +} \ No newline at end of file diff --git a/backends/conrod_crayon/tools/profiler/README.md b/backends/conrod_crayon/tools/profiler/README.md new file mode 100644 index 000000000..eb30f5812 --- /dev/null +++ b/backends/conrod_crayon/tools/profiler/README.md @@ -0,0 +1,8 @@ +# Profiler + +This script is a simple wrapper of [dtrace](http://dtrace.org/) and [flamegraph](https://github.com/brendangregg/FlameGraph). + +```sh +$ # Notes that dtrace requires additional privileges. +$ sudo ./tools/profiler/run.sh target/release/saturn out.svg +``` \ No newline at end of file diff --git a/backends/conrod_crayon/tools/profiler/run.sh b/backends/conrod_crayon/tools/profiler/run.sh new file mode 100755 index 000000000..a1e6e3752 --- /dev/null +++ b/backends/conrod_crayon/tools/profiler/run.sh @@ -0,0 +1,17 @@ +USAGE="Usage: sudo ./tools/profiler/run.sh [EXECUTABLE_FILE] [OUT_FILE]" + +if [ "$#" -ne 2 ]; then + echo $USAGE + exit 1 +fi + +EXECUTABLE_FILE=$1 +OUT_FILE=$2 +SCRIPT_FILE="$( cd "$(dirname "$0")" ; pwd -P )" +NAME=$(basename $EXECUTABLE_FILE) + +set -e +rm -f out.stacks +dtrace -c $EXECUTABLE_FILE -o out.stacks -n "profile-30 /execname == \"${NAME}\"/ { @[ustack(1000)] = count(); }" +$SCRIPT_FILE/scripts/stackcollapse.pl out.stacks | $SCRIPT_FILE/scripts/flamegraph.pl > $OUT_FILE +rm -f out.stacks \ No newline at end of file diff --git a/backends/conrod_crayon/tools/profiler/scripts/cddl1.txt b/backends/conrod_crayon/tools/profiler/scripts/cddl1.txt new file mode 100644 index 000000000..da23621dc --- /dev/null +++ b/backends/conrod_crayon/tools/profiler/scripts/cddl1.txt @@ -0,0 +1,384 @@ +Unless otherwise noted, all files in this distribution are released +under the Common Development and Distribution License (CDDL). +Exceptions are noted within the associated source files. + +-------------------------------------------------------------------- + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + +-------------------------------------------------------------------- + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND +DISTRIBUTION LICENSE (CDDL) + +For Covered Software in this distribution, this License shall +be governed by the laws of the State of California (excluding +conflict-of-law provisions). + +Any litigation relating to this License shall be subject to the +jurisdiction of the Federal Courts of the Northern District of +California and the state courts of the State of California, with +venue lying in Santa Clara County, California. diff --git a/backends/conrod_crayon/tools/profiler/scripts/flamegraph.pl b/backends/conrod_crayon/tools/profiler/scripts/flamegraph.pl new file mode 100755 index 000000000..ac3819ccf --- /dev/null +++ b/backends/conrod_crayon/tools/profiler/scripts/flamegraph.pl @@ -0,0 +1,1125 @@ +#!/usr/bin/perl -w +# +# flamegraph.pl flame stack grapher. +# +# This takes stack samples and renders a call graph, allowing hot functions +# and codepaths to be quickly identified. Stack samples can be generated using +# tools such as DTrace, perf, SystemTap, and Instruments. +# +# USAGE: ./flamegraph.pl [options] input.txt > graph.svg +# +# grep funcA input.txt | ./flamegraph.pl [options] > graph.svg +# +# Then open the resulting .svg in a web browser, for interactivity: mouse-over +# frames for info, click to zoom, and ctrl-F to search. +# +# Options are listed in the usage message (--help). +# +# The input is stack frames and sample counts formatted as single lines. Each +# frame in the stack is semicolon separated, with a space and count at the end +# of the line. These can be generated for Linux perf script output using +# stackcollapse-perf.pl, for DTrace using stackcollapse.pl, and for other tools +# using the other stackcollapse programs. Example input: +# +# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 1 +# +# An optional extra column of counts can be provided to generate a differential +# flame graph of the counts, colored red for more, and blue for less. This +# can be useful when using flame graphs for non-regression testing. +# See the header comment in the difffolded.pl program for instructions. +# +# The input functions can optionally have annotations at the end of each +# function name, following a precedent by some tools (Linux perf's _[k]): +# _[k] for kernel +# _[i] for inlined +# _[j] for jit +# _[w] for waker +# Some of the stackcollapse programs support adding these annotations, eg, +# stackcollapse-perf.pl --kernel --jit. They are used merely for colors by +# some palettes, eg, flamegraph.pl --color=java. +# +# The output flame graph shows relative presence of functions in stack samples. +# The ordering on the x-axis has no meaning; since the data is samples, time +# order of events is not known. The order used sorts function names +# alphabetically. +# +# While intended to process stack samples, this can also process stack traces. +# For example, tracing stacks for memory allocation, or resource usage. You +# can use --title to set the title to reflect the content, and --countname +# to change "samples" to "bytes" etc. +# +# There are a few different palettes, selectable using --color. By default, +# the colors are selected at random (except for differentials). Functions +# called "-" will be printed gray, which can be used for stack separators (eg, +# between user and kernel stacks). +# +# HISTORY +# +# This was inspired by Neelakanth Nadgir's excellent function_call_graph.rb +# program, which visualized function entry and return trace events. As Neel +# wrote: "The output displayed is inspired by Roch's CallStackAnalyzer which +# was in turn inspired by the work on vftrace by Jan Boerhout". See: +# https://blogs.oracle.com/realneel/entry/visualizing_callstacks_via_dtrace_and +# +# Copyright 2016 Netflix, Inc. +# Copyright 2011 Joyent, Inc. All rights reserved. +# Copyright 2011 Brendan Gregg. All rights reserved. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at docs/cddl1.txt or +# http://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at docs/cddl1.txt. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# 11-Oct-2014 Adrien Mahieux Added zoom. +# 21-Nov-2013 Shawn Sterling Added consistent palette file option +# 17-Mar-2013 Tim Bunce Added options and more tunables. +# 15-Dec-2011 Dave Pacheco Support for frames with whitespace. +# 10-Sep-2011 Brendan Gregg Created this. + +use strict; + +use Getopt::Long; + +use open qw(:std :utf8); + +# tunables +my $encoding; +my $fonttype = "Verdana"; +my $imagewidth = 1200; # max width, pixels +my $frameheight = 16; # max height is dynamic +my $fontsize = 12; # base text size +my $fontwidth = 0.59; # avg width relative to fontsize +my $minwidth = 0.1; # min function width, pixels +my $nametype = "Function:"; # what are the names in the data? +my $countname = "samples"; # what are the counts in the data? +my $colors = "hot"; # color theme +my $bgcolor1 = "#eeeeee"; # background color gradient start +my $bgcolor2 = "#eeeeb0"; # background color gradient stop +my $nameattrfile; # file holding function attributes +my $timemax; # (override the) sum of the counts +my $factor = 1; # factor to scale counts by +my $hash = 0; # color by function name +my $palette = 0; # if we use consistent palettes (default off) +my %palette_map; # palette map hash +my $pal_file = "palette.map"; # palette map file name +my $stackreverse = 0; # reverse stack order, switching merge end +my $inverted = 0; # icicle graph +my $negate = 0; # switch differential hues +my $titletext = ""; # centered heading +my $titledefault = "Flame Graph"; # overwritten by --title +my $titleinverted = "Icicle Graph"; # " " +my $searchcolor = "rgb(230,0,230)"; # color for search highlighting +my $notestext = ""; # embedded notes in SVG +my $subtitletext = ""; # second level title (optional) +my $help = 0; + +sub usage { + die < outfile.svg\n + --title TEXT # change title text + --subtitle TEXT # second level title (optional) + --width NUM # width of image (default 1200) + --height NUM # height of each frame (default 16) + --minwidth NUM # omit smaller functions (default 0.1 pixels) + --fonttype FONT # font type (default "Verdana") + --fontsize NUM # font size (default 12) + --countname TEXT # count type label (default "samples") + --nametype TEXT # name type label (default "Function:") + --colors PALETTE # set color palette. choices are: hot (default), mem, + # io, wakeup, chain, java, js, perl, red, green, blue, + # aqua, yellow, purple, orange + --hash # colors are keyed by function name hash + --cp # use consistent palette (palette.map) + --reverse # generate stack-reversed flame graph + --inverted # icicle graph + --negate # switch differential hues (blue<->red) + --notes TEXT # add notes comment in SVG (for debugging) + --help # this message + + eg, + $0 --title="Flame Graph: malloc()" trace.txt > graph.svg +USAGE_END +} + +GetOptions( + 'fonttype=s' => \$fonttype, + 'width=i' => \$imagewidth, + 'height=i' => \$frameheight, + 'encoding=s' => \$encoding, + 'fontsize=f' => \$fontsize, + 'fontwidth=f' => \$fontwidth, + 'minwidth=f' => \$minwidth, + 'title=s' => \$titletext, + 'subtitle=s' => \$subtitletext, + 'nametype=s' => \$nametype, + 'countname=s' => \$countname, + 'nameattr=s' => \$nameattrfile, + 'total=s' => \$timemax, + 'factor=f' => \$factor, + 'colors=s' => \$colors, + 'hash' => \$hash, + 'cp' => \$palette, + 'reverse' => \$stackreverse, + 'inverted' => \$inverted, + 'negate' => \$negate, + 'notes=s' => \$notestext, + 'help' => \$help, +) or usage(); +$help && usage(); + +# internals +my $ypad1 = $fontsize * 3; # pad top, include title +my $ypad2 = $fontsize * 2 + 10; # pad bottom, include labels +my $ypad3 = $fontsize * 2; # pad top, include subtitle (optional) +my $xpad = 10; # pad lefm and right +my $framepad = 1; # vertical padding for frames +my $depthmax = 0; +my %Events; +my %nameattr; + +if ($titletext eq "") { + unless ($inverted) { + $titletext = $titledefault; + } else { + $titletext = $titleinverted; + } +} + +if ($nameattrfile) { + # The name-attribute file format is a function name followed by a tab then + # a sequence of tab separated name=value pairs. + open my $attrfh, $nameattrfile or die "Can't read $nameattrfile: $!\n"; + while (<$attrfh>) { + chomp; + my ($funcname, $attrstr) = split /\t/, $_, 2; + die "Invalid format in $nameattrfile" unless defined $attrstr; + $nameattr{$funcname} = { map { split /=/, $_, 2 } split /\t/, $attrstr }; + } +} + +if ($notestext =~ /[<>]/) { + die "Notes string can't contain < or >" +} + +# background colors: +# - yellow gradient: default (hot, java, js, perl) +# - blue gradient: mem, chain +# - gray gradient: io, wakeup, flat colors (red, green, blue, ...) +if ($colors eq "mem" or $colors eq "chain") { + $bgcolor1 = "#eeeeee"; $bgcolor2 = "#e0e0ff"; +} +if ($colors =~ /^(io|wakeup|red|green|blue|aqua|yellow|purple|orange)$/) { + $bgcolor1 = "#f8f8f8"; $bgcolor2 = "#e8e8e8"; +} + +# SVG functions +{ package SVG; + sub new { + my $class = shift; + my $self = {}; + bless ($self, $class); + return $self; + } + + sub header { + my ($self, $w, $h) = @_; + my $enc_attr = ''; + if (defined $encoding) { + $enc_attr = qq{ encoding="$encoding"}; + } + $self->{svg} .= < + + + + +SVG + } + + sub include { + my ($self, $content) = @_; + $self->{svg} .= $content; + } + + sub colorAllocate { + my ($self, $r, $g, $b) = @_; + return "rgb($r,$g,$b)"; + } + + sub group_start { + my ($self, $attr) = @_; + + my @g_attr = map { + exists $attr->{$_} ? sprintf(qq/$_="%s"/, $attr->{$_}) : () + } qw(class style onmouseover onmouseout onclick); + push @g_attr, $attr->{g_extra} if $attr->{g_extra}; + $self->{svg} .= sprintf qq/\n/, join(' ', @g_attr); + + $self->{svg} .= sprintf qq/%s<\/title>/, $attr->{title} + if $attr->{title}; # should be first element within g container + + if ($attr->{href}) { + my @a_attr; + push @a_attr, sprintf qq/xlink:href="%s"/, $attr->{href} if $attr->{href}; + # default target=_top else links will open within SVG + push @a_attr, sprintf qq/target="%s"/, $attr->{target} || "_top"; + push @a_attr, $attr->{a_extra} if $attr->{a_extra}; + $self->{svg} .= sprintf qq//, join(' ', @a_attr); + } + } + + sub group_end { + my ($self, $attr) = @_; + $self->{svg} .= qq/<\/a>\n/ if $attr->{href}; + $self->{svg} .= qq/<\/g>\n/; + } + + sub filledRectangle { + my ($self, $x1, $y1, $x2, $y2, $fill, $extra) = @_; + $x1 = sprintf "%0.1f", $x1; + $x2 = sprintf "%0.1f", $x2; + my $w = sprintf "%0.1f", $x2 - $x1; + my $h = sprintf "%0.1f", $y2 - $y1; + $extra = defined $extra ? $extra : ""; + $self->{svg} .= qq/\n/; + } + + sub stringTTF { + my ($self, $color, $font, $size, $angle, $x, $y, $str, $loc, $extra) = @_; + $x = sprintf "%0.2f", $x; + $loc = defined $loc ? $loc : "left"; + $extra = defined $extra ? $extra : ""; + $self->{svg} .= qq/$str<\/text>\n/; + } + + sub svg { + my $self = shift; + return "$self->{svg}\n"; + } + 1; +} + +sub namehash { + # Generate a vector hash for the name string, weighting early over + # later characters. We want to pick the same colors for function + # names across different flame graphs. + my $name = shift; + my $vector = 0; + my $weight = 1; + my $max = 1; + my $mod = 10; + # if module name present, trunc to 1st char + $name =~ s/.(.*?)`//; + foreach my $c (split //, $name) { + my $i = (ord $c) % $mod; + $vector += ($i / ($mod++ - 1)) * $weight; + $max += 1 * $weight; + $weight *= 0.70; + last if $mod > 12; + } + return (1 - $vector / $max) +} + +sub color { + my ($type, $hash, $name) = @_; + my ($v1, $v2, $v3); + + if ($hash) { + $v1 = namehash($name); + $v2 = $v3 = namehash(scalar reverse $name); + } else { + $v1 = rand(1); + $v2 = rand(1); + $v3 = rand(1); + } + + # theme palettes + if (defined $type and $type eq "hot") { + my $r = 205 + int(50 * $v3); + my $g = 0 + int(230 * $v1); + my $b = 0 + int(55 * $v2); + return "rgb($r,$g,$b)"; + } + if (defined $type and $type eq "mem") { + my $r = 0; + my $g = 190 + int(50 * $v2); + my $b = 0 + int(210 * $v1); + return "rgb($r,$g,$b)"; + } + if (defined $type and $type eq "io") { + my $r = 80 + int(60 * $v1); + my $g = $r; + my $b = 190 + int(55 * $v2); + return "rgb($r,$g,$b)"; + } + + # multi palettes + if (defined $type and $type eq "java") { + # Handle both annotations (_[j], _[i], ...; which are + # accurate), as well as input that lacks any annotations, as + # best as possible. Without annotations, we get a little hacky + # and match on java|org|com, etc. + if ($name =~ m:_\[j\]$:) { # jit annotation + $type = "green"; + } elsif ($name =~ m:_\[i\]$:) { # inline annotation + $type = "aqua"; + } elsif ($name =~ m:^L?(java|org|com|io|sun)/:) { # Java + $type = "green"; + } elsif ($name =~ m:_\[k\]$:) { # kernel annotation + $type = "orange"; + } elsif ($name =~ /::/) { # C++ + $type = "yellow"; + } else { # system + $type = "red"; + } + # fall-through to color palettes + } + if (defined $type and $type eq "perl") { + if ($name =~ /::/) { # C++ + $type = "yellow"; + } elsif ($name =~ m:Perl: or $name =~ m:\.pl:) { # Perl + $type = "green"; + } elsif ($name =~ m:_\[k\]$:) { # kernel + $type = "orange"; + } else { # system + $type = "red"; + } + # fall-through to color palettes + } + if (defined $type and $type eq "js") { + # Handle both annotations (_[j], _[i], ...; which are + # accurate), as well as input that lacks any annotations, as + # best as possible. Without annotations, we get a little hacky, + # and match on a "/" with a ".js", etc. + if ($name =~ m:_\[j\]$:) { # jit annotation + if ($name =~ m:/:) { + $type = "green"; # source + } else { + $type = "aqua"; # builtin + } + } elsif ($name =~ /::/) { # C++ + $type = "yellow"; + } elsif ($name =~ m:/.*\.js:) { # JavaScript (match "/" in path) + $type = "green"; + } elsif ($name =~ m/:/) { # JavaScript (match ":" in builtin) + $type = "aqua"; + } elsif ($name =~ m/^ $/) { # Missing symbol + $type = "green"; + } elsif ($name =~ m:_\[k\]:) { # kernel + $type = "orange"; + } else { # system + $type = "red"; + } + # fall-through to color palettes + } + if (defined $type and $type eq "wakeup") { + $type = "aqua"; + # fall-through to color palettes + } + if (defined $type and $type eq "chain") { + if ($name =~ m:_\[w\]:) { # waker + $type = "aqua" + } else { # off-CPU + $type = "blue"; + } + # fall-through to color palettes + } + + # color palettes + if (defined $type and $type eq "red") { + my $r = 200 + int(55 * $v1); + my $x = 50 + int(80 * $v1); + return "rgb($r,$x,$x)"; + } + if (defined $type and $type eq "green") { + my $g = 200 + int(55 * $v1); + my $x = 50 + int(60 * $v1); + return "rgb($x,$g,$x)"; + } + if (defined $type and $type eq "blue") { + my $b = 205 + int(50 * $v1); + my $x = 80 + int(60 * $v1); + return "rgb($x,$x,$b)"; + } + if (defined $type and $type eq "yellow") { + my $x = 175 + int(55 * $v1); + my $b = 50 + int(20 * $v1); + return "rgb($x,$x,$b)"; + } + if (defined $type and $type eq "purple") { + my $x = 190 + int(65 * $v1); + my $g = 80 + int(60 * $v1); + return "rgb($x,$g,$x)"; + } + if (defined $type and $type eq "aqua") { + my $r = 50 + int(60 * $v1); + my $g = 165 + int(55 * $v1); + my $b = 165 + int(55 * $v1); + return "rgb($r,$g,$b)"; + } + if (defined $type and $type eq "orange") { + my $r = 190 + int(65 * $v1); + my $g = 90 + int(65 * $v1); + return "rgb($r,$g,0)"; + } + + return "rgb(0,0,0)"; +} + +sub color_scale { + my ($value, $max) = @_; + my ($r, $g, $b) = (255, 255, 255); + $value = -$value if $negate; + if ($value > 0) { + $g = $b = int(210 * ($max - $value) / $max); + } elsif ($value < 0) { + $r = $g = int(210 * ($max + $value) / $max); + } + return "rgb($r,$g,$b)"; +} + +sub color_map { + my ($colors, $func) = @_; + if (exists $palette_map{$func}) { + return $palette_map{$func}; + } else { + $palette_map{$func} = color($colors, $hash, $func); + return $palette_map{$func}; + } +} + +sub write_palette { + open(FILE, ">$pal_file"); + foreach my $key (sort keys %palette_map) { + print FILE $key."->".$palette_map{$key}."\n"; + } + close(FILE); +} + +sub read_palette { + if (-e $pal_file) { + open(FILE, $pal_file) or die "can't open file $pal_file: $!"; + while ( my $line = ) { + chomp($line); + (my $key, my $value) = split("->",$line); + $palette_map{$key}=$value; + } + close(FILE) + } +} + +my %Node; # Hash of merged frame data +my %Tmp; + +# flow() merges two stacks, storing the merged frames and value data in %Node. +sub flow { + my ($last, $this, $v, $d) = @_; + + my $len_a = @$last - 1; + my $len_b = @$this - 1; + + my $i = 0; + my $len_same; + for (; $i <= $len_a; $i++) { + last if $i > $len_b; + last if $last->[$i] ne $this->[$i]; + } + $len_same = $i; + + for ($i = $len_a; $i >= $len_same; $i--) { + my $k = "$last->[$i];$i"; + # a unique ID is constructed from "func;depth;etime"; + # func-depth isn't unique, it may be repeated later. + $Node{"$k;$v"}->{stime} = delete $Tmp{$k}->{stime}; + if (defined $Tmp{$k}->{delta}) { + $Node{"$k;$v"}->{delta} = delete $Tmp{$k}->{delta}; + } + delete $Tmp{$k}; + } + + for ($i = $len_same; $i <= $len_b; $i++) { + my $k = "$this->[$i];$i"; + $Tmp{$k}->{stime} = $v; + if (defined $d) { + $Tmp{$k}->{delta} += $i == $len_b ? $d : 0; + } + } + + return $this; +} + +# parse input +my @Data; +my $last = []; +my $time = 0; +my $delta = undef; +my $ignored = 0; +my $line; +my $maxdelta = 1; + +# reverse if needed +foreach (<>) { + chomp; + $line = $_; + if ($stackreverse) { + # there may be an extra samples column for differentials + # XXX todo: redo these REs as one. It's repeated below. + my($stack, $samples) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); + my $samples2 = undef; + if ($stack =~ /^(.*)\s+?(\d+(?:\.\d*)?)$/) { + $samples2 = $samples; + ($stack, $samples) = $stack =~ (/^(.*)\s+?(\d+(?:\.\d*)?)$/); + unshift @Data, join(";", reverse split(";", $stack)) . " $samples $samples2"; + } else { + unshift @Data, join(";", reverse split(";", $stack)) . " $samples"; + } + } else { + unshift @Data, $line; + } +} + +# process and merge frames +foreach (sort @Data) { + chomp; + # process: folded_stack count + # eg: func_a;func_b;func_c 31 + my ($stack, $samples) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); + unless (defined $samples and defined $stack) { + ++$ignored; + next; + } + + # there may be an extra samples column for differentials: + my $samples2 = undef; + if ($stack =~ /^(.*)\s+?(\d+(?:\.\d*)?)$/) { + $samples2 = $samples; + ($stack, $samples) = $stack =~ (/^(.*)\s+?(\d+(?:\.\d*)?)$/); + } + $delta = undef; + if (defined $samples2) { + $delta = $samples2 - $samples; + $maxdelta = abs($delta) if abs($delta) > $maxdelta; + } + + # for chain graphs, annotate waker frames with "_[w]", for later + # coloring. This is a hack, but has a precedent ("_[k]" from perf). + if ($colors eq "chain") { + my @parts = split ";--;", $stack; + my @newparts = (); + $stack = shift @parts; + $stack .= ";--;"; + foreach my $part (@parts) { + $part =~ s/;/_[w];/g; + $part .= "_[w]"; + push @newparts, $part; + } + $stack .= join ";--;", @parts; + } + + # merge frames and populate %Node: + $last = flow($last, [ '', split ";", $stack ], $time, $delta); + + if (defined $samples2) { + $time += $samples2; + } else { + $time += $samples; + } +} +flow($last, [], $time, $delta); + +warn "Ignored $ignored lines with invalid format\n" if $ignored; +unless ($time) { + warn "ERROR: No stack counts found\n"; + my $im = SVG->new(); + # emit an error message SVG, for tools automating flamegraph use + my $imageheight = $fontsize * 5; + $im->header($imagewidth, $imageheight); + $im->stringTTF($im->colorAllocate(0, 0, 0), $fonttype, $fontsize + 2, + 0.0, int($imagewidth / 2), $fontsize * 2, + "ERROR: No valid input provided to flamegraph.pl.", "middle"); + print $im->svg; + exit 2; +} +if ($timemax and $timemax < $time) { + warn "Specified --total $timemax is less than actual total $time, so ignored\n" + if $timemax/$time > 0.02; # only warn is significant (e.g., not rounding etc) + undef $timemax; +} +$timemax ||= $time; + +my $widthpertime = ($imagewidth - 2 * $xpad) / $timemax; +my $minwidth_time = $minwidth / $widthpertime; + +# prune blocks that are too narrow and determine max depth +while (my ($id, $node) = each %Node) { + my ($func, $depth, $etime) = split ";", $id; + my $stime = $node->{stime}; + die "missing start for $id" if not defined $stime; + + if (($etime-$stime) < $minwidth_time) { + delete $Node{$id}; + next; + } + $depthmax = $depth if $depth > $depthmax; +} + +# draw canvas, and embed interactive JavaScript program +my $imageheight = (($depthmax + 1) * $frameheight) + $ypad1 + $ypad2; +$imageheight += $ypad3 if $subtitletext ne ""; +my $im = SVG->new(); +$im->header($imagewidth, $imageheight); +my $inc = < + + + + + + + +INC +$im->include($inc); +$im->filledRectangle(0, 0, $imagewidth, $imageheight, 'url(#background)'); +my ($white, $black, $vvdgrey, $vdgrey, $dgrey) = ( + $im->colorAllocate(255, 255, 255), + $im->colorAllocate(0, 0, 0), + $im->colorAllocate(40, 40, 40), + $im->colorAllocate(160, 160, 160), + $im->colorAllocate(200, 200, 200), + ); +$im->stringTTF($black, $fonttype, $fontsize + 5, 0.0, int($imagewidth / 2), $fontsize * 2, $titletext, "middle"); +if ($subtitletext ne "") { + $im->stringTTF($vdgrey, $fonttype, $fontsize, 0.0, int($imagewidth / 2), $fontsize * 4, $subtitletext, "middle"); +} +$im->stringTTF($black, $fonttype, $fontsize, 0.0, $xpad, $imageheight - ($ypad2 / 2), " ", "", 'id="details"'); +$im->stringTTF($black, $fonttype, $fontsize, 0.0, $xpad, $fontsize * 2, + "Reset Zoom", "", 'id="unzoom" onclick="unzoom()" style="opacity:0.0;cursor:pointer"'); +$im->stringTTF($black, $fonttype, $fontsize, 0.0, $imagewidth - $xpad - 100, + $fontsize * 2, "Search", "", 'id="search" onmouseover="searchover()" onmouseout="searchout()" onclick="search_prompt()" style="opacity:0.1;cursor:pointer"'); +$im->stringTTF($black, $fonttype, $fontsize, 0.0, $imagewidth - $xpad - 100, $imageheight - ($ypad2 / 2), " ", "", 'id="matched"'); + +if ($palette) { + read_palette(); +} + +# draw frames +while (my ($id, $node) = each %Node) { + my ($func, $depth, $etime) = split ";", $id; + my $stime = $node->{stime}; + my $delta = $node->{delta}; + + $etime = $timemax if $func eq "" and $depth == 0; + + my $x1 = $xpad + $stime * $widthpertime; + my $x2 = $xpad + $etime * $widthpertime; + my ($y1, $y2); + unless ($inverted) { + $y1 = $imageheight - $ypad2 - ($depth + 1) * $frameheight + $framepad; + $y2 = $imageheight - $ypad2 - $depth * $frameheight; + } else { + $y1 = $ypad1 + $depth * $frameheight; + $y2 = $ypad1 + ($depth + 1) * $frameheight - $framepad; + } + + my $samples = sprintf "%.0f", ($etime - $stime) * $factor; + (my $samples_txt = $samples) # add commas per perlfaq5 + =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g; + + my $info; + if ($func eq "" and $depth == 0) { + $info = "all ($samples_txt $countname, 100%)"; + } else { + my $pct = sprintf "%.2f", ((100 * $samples) / ($timemax * $factor)); + my $escaped_func = $func; + # clean up SVG breaking characters: + $escaped_func =~ s/&/&/g; + $escaped_func =~ s//>/g; + $escaped_func =~ s/"/"/g; + $escaped_func =~ s/_\[[kwij]\]$//; # strip any annotation + unless (defined $delta) { + $info = "$escaped_func ($samples_txt $countname, $pct%)"; + } else { + my $d = $negate ? -$delta : $delta; + my $deltapct = sprintf "%.2f", ((100 * $d) / ($timemax * $factor)); + $deltapct = $d > 0 ? "+$deltapct" : $deltapct; + $info = "$escaped_func ($samples_txt $countname, $pct%; $deltapct%)"; + } + } + + my $nameattr = { %{ $nameattr{$func}||{} } }; # shallow clone + $nameattr->{class} ||= "func_g"; + $nameattr->{onmouseover} ||= "s(this)"; + $nameattr->{onmouseout} ||= "c()"; + $nameattr->{onclick} ||= "zoom(this)"; + $nameattr->{title} ||= $info; + $im->group_start($nameattr); + + my $color; + if ($func eq "--") { + $color = $vdgrey; + } elsif ($func eq "-") { + $color = $dgrey; + } elsif (defined $delta) { + $color = color_scale($delta, $maxdelta); + } elsif ($palette) { + $color = color_map($colors, $func); + } else { + $color = color($colors, $hash, $func); + } + $im->filledRectangle($x1, $y1, $x2, $y2, $color, 'rx="2" ry="2"'); + + my $chars = int( ($x2 - $x1) / ($fontsize * $fontwidth)); + my $text = ""; + if ($chars >= 3) { # room for one char plus two dots + $func =~ s/_\[[kwij]\]$//; # strip any annotation + $text = substr $func, 0, $chars; + substr($text, -2, 2) = ".." if $chars < length $func; + $text =~ s/&/&/g; + $text =~ s//>/g; + } + $im->stringTTF($black, $fonttype, $fontsize, 0.0, $x1 + 3, 3 + ($y1 + $y2) / 2, $text, ""); + + $im->group_end($nameattr); +} + +print $im->svg; + +if ($palette) { + write_palette(); +} + +# vim: ts=8 sts=8 sw=8 noexpandtab diff --git a/backends/conrod_crayon/tools/profiler/scripts/stackcollapse.pl b/backends/conrod_crayon/tools/profiler/scripts/stackcollapse.pl new file mode 100755 index 000000000..17144bb62 --- /dev/null +++ b/backends/conrod_crayon/tools/profiler/scripts/stackcollapse.pl @@ -0,0 +1,109 @@ +#!/usr/bin/perl -w +# +# stackcolllapse.pl collapse multiline stacks into single lines. +# +# Parses a multiline stack followed by a number on a separate line, and +# outputs a semicolon separated stack followed by a space and the number. +# If memory addresses (+0xd) are present, they are stripped, and resulting +# identical stacks are colased with their counts summed. +# +# USAGE: ./stackcollapse.pl infile > outfile +# +# Example input: +# +# unix`i86_mwait+0xd +# unix`cpu_idle_mwait+0xf1 +# unix`idle+0x114 +# unix`thread_start+0x8 +# 1641 +# +# Example output: +# +# unix`thread_start;unix`idle;unix`cpu_idle_mwait;unix`i86_mwait 1641 +# +# Input may contain many stacks, and can be generated using DTrace. The +# first few lines of input are skipped (see $headerlines). +# +# Copyright 2011 Joyent, Inc. All rights reserved. +# Copyright 2011 Brendan Gregg. All rights reserved. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at docs/cddl1.txt or +# http://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at docs/cddl1.txt. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# 14-Aug-2011 Brendan Gregg Created this. + +use strict; + +my $headerlines = 3; # number of input lines to skip +my $includeoffset = 0; # include function offset (except leafs) +my %collapsed; + +sub remember_stack { + my ($stack, $count) = @_; + $collapsed{$stack} += $count; +} + +my $nr = 0; +my @stack; + +foreach (<>) { + next if $nr++ < $headerlines; + chomp; + + if (m/^\s*(\d+)+$/) { + my $count = $1; + my $joined = join(";", @stack); + + # trim leaf offset if these were retained: + $joined =~ s/\+[^+]*$// if $includeoffset; + + remember_stack($joined, $count); + @stack = (); + next; + } + + next if (m/^\s*$/); + + my $frame = $_; + $frame =~ s/^\s*//; + $frame =~ s/\+[^+]*$// unless $includeoffset; + + # Remove arguments from C++ function names: + $frame =~ s/(::.*)[(<].*/$1/; + + $frame = "-" if $frame eq ""; + + my @inline; + for (split /\->/, $frame) { + my $func = $_; + + # Strip out L and ; included in java stacks + $func =~ tr/\;/:/; + $func =~ s/^L//; + $func .= "_[i]" if scalar(@inline) > 0; #inlined + + push @inline, $func; + } + + unshift @stack, @inline; +} + +foreach my $k (sort { $a cmp $b } keys %collapsed) { + print "$k $collapsed{$k}\n"; +} diff --git a/backends/conrod_crayon/tools/wasm-template/README.md b/backends/conrod_crayon/tools/wasm-template/README.md new file mode 100644 index 000000000..60fb75c9f --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/README.md @@ -0,0 +1,9 @@ +# Wasm-Template +You will need to install crayon-cli. You may cargo install crayon-cli. +```sh +$ cd assets/crayon +$ crayon-cli build +$ cd ../../tools/wasm-template +$ npm install +$ ./run.sh crayon +``` \ No newline at end of file diff --git a/backends/conrod_crayon/tools/wasm-template/app.js b/backends/conrod_crayon/tools/wasm-template/app.js new file mode 100644 index 000000000..1b5a899b8 --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/app.js @@ -0,0 +1,14 @@ + +var port = process.env.PORT || 8080; +const config = require('./webpack.config.js') +const webpack = require('webpack'); +const middleware = require('webpack-dev-middleware'); +const compiler = webpack(config); +const express = require('express'); +const app = express(); + +app.use(middleware(compiler, { + // webpack-dev-middleware options t +})); + +app.listen(port, () => console.log('Example app listening on port 8080!')) \ No newline at end of file diff --git a/backends/conrod_crayon/tools/wasm-template/env.js b/backends/conrod_crayon/tools/wasm-template/env.js new file mode 100644 index 000000000..da28753ec --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/env.js @@ -0,0 +1,17 @@ + +class env{ + constructor(){ + function rust_wasm_syscall (index, data) { + console.log("rust_wasm_syscall", index, data); + // See https://github.com/rust-lang/rust/blob/master/src/libstd/sys/wasm/mod.rs + switch (index) { + case 6: + return 1; + default: + return 0; + } + } + } + +} +export default env; \ No newline at end of file diff --git a/backends/conrod_crayon/tools/wasm-template/index.html b/backends/conrod_crayon/tools/wasm-template/index.html new file mode 100644 index 000000000..3dfae1b3e --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/index.html @@ -0,0 +1,12 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/backends/conrod_crayon/tools/wasm-template/index.js b/backends/conrod_crayon/tools/wasm-template/index.js new file mode 100644 index 000000000..2732dc302 --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/index.js @@ -0,0 +1,35 @@ +import init from "./dist/native.js"; + +// FIXME dirty hack for wasm_syscall +const env = { + rust_wasm_syscall: (index, data) => { + console.log("rust_wasm_syscall", index, data); + // See https://github.com/rust-lang/rust/blob/master/src/libstd/sys/wasm/mod.rs + switch (index) { + case 6: + return 1; + default: + return 0; + } + } +}; +const instantiateStreaming = WebAssembly.instantiateStreaming; +WebAssembly.instantiateStreaming = (source, importObject) => + instantiateStreaming(source, { + ...importObject, + env + }); +const instantiate = WebAssembly.instantiate; +WebAssembly.instantiate = (bufferSource, importObject) => + instantiate(bufferSource, { + ...importObject, + env + }); + +Promise.all([ + init("./dist/native_bg.wasm") +]).then(function(){ + console.log("done"); +}); + +// vim: set ts=2 sw=2 et: diff --git a/backends/conrod_crayon/tools/wasm-template/package.json b/backends/conrod_crayon/tools/wasm-template/package.json new file mode 100644 index 000000000..f85d54ed4 --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/package.json @@ -0,0 +1,17 @@ +{ + "scripts": { + "build": "webpack -p", + "serve": "webpack-dev-server -p" + }, + "devDependencies": { + "html-webpack-plugin": "^3.2.0", + "text-encoding": "^0.7.0", + "webpack": "^4.35.0", + "webpack-cli": "^3.3.4", + "webpack-dev-server": "^3.7.2" + }, + "dependencies": { + "@open-wc/webpack-import-meta-loader": "^0.2.4", + "ws": "^7.0.0" + } +} diff --git a/backends/conrod_crayon/tools/wasm-template/run.sh b/backends/conrod_crayon/tools/wasm-template/run.sh new file mode 100755 index 000000000..72d909a71 --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/run.sh @@ -0,0 +1,12 @@ +SCRIPT_DIR=$(dirname "$0") +BASE_DIR=$SCRIPT_DIR/../../../../ + +set -e +cargo build --example $1 --target wasm32-unknown-unknown +rm -rf $SCRIPT_DIR/dist +mkdir $SCRIPT_DIR/dist +mkdir $SCRIPT_DIR/dist/intermediate +cp $BASE_DIR/target/wasm32-unknown-unknown/debug/examples/$1.wasm $SCRIPT_DIR/dist/intermediate/native.wasm +wasm-bindgen --target web $SCRIPT_DIR/dist/intermediate/native.wasm --out-dir $SCRIPT_DIR/dist --no-typescript +cd $SCRIPT_DIR +npm run serve \ No newline at end of file diff --git a/backends/conrod_crayon/tools/wasm-template/webpack.config.js b/backends/conrod_crayon/tools/wasm-template/webpack.config.js new file mode 100644 index 000000000..510849928 --- /dev/null +++ b/backends/conrod_crayon/tools/wasm-template/webpack.config.js @@ -0,0 +1,39 @@ +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const webpack = require('webpack'); + +module.exports = { + entry: './index.js', + output: { + path: path.resolve(__dirname, 'dist'), + filename: 'index.js', + }, + plugins: [ + new HtmlWebpackPlugin({ + template: "index.html" + }), + // Have this example work in Edge which doesn't ship `TextEncoder` or + // `TextDecoder` at this time. + new webpack.ProvidePlugin({ + TextDecoder: ['text-encoding', 'TextDecoder'], + TextEncoder: ['text-encoding', 'TextEncoder'] + }) + ], + devServer: { + contentBase: [__dirname, path.join(__dirname, '../../../../assets/crayon')], + port: 8080 + }, + node: { + fs: "empty" + }, + module: { + rules: [ + { + test: /\.js$/, + loader: require.resolve('@open-wc/webpack-import-meta-loader'), + }, + ], + }, + externals: [ 'utf-8-validate', 'bufferutil'], + mode: 'development' +}; \ No newline at end of file diff --git a/backends/conrod_example_shared/src/lib.rs b/backends/conrod_example_shared/src/lib.rs index cd1a2586e..6993a890e 100644 --- a/backends/conrod_example_shared/src/lib.rs +++ b/backends/conrod_example_shared/src/lib.rs @@ -21,7 +21,8 @@ pub struct DemoApp { ball_xy: conrod_core::Point, ball_color: conrod_core::Color, sine_frequency: f32, - rust_logo: conrod_core::image::Id, + pub rust_logo: conrod_core::image::Id, + pub textedit: String } @@ -33,6 +34,7 @@ impl DemoApp { ball_color: conrod_core::color::WHITE, sine_frequency: 1.0, rust_logo: rust_logo, + textedit: "".to_owned() } } } @@ -97,6 +99,7 @@ widget_ids! { plot_path, // Scrollbar canvas_scrollbar, + text_edit } } @@ -177,7 +180,7 @@ pub fn gui(ui: &mut conrod_core::UiCell, ids: &Ids, app: &mut DemoApp) { let radius = 10.0; widget::RoundedRectangle::fill([w, h], radius) .color(conrod_core::color::CHARCOAL.alpha(0.25)) - .middle_of(ids.shapes_canvas) + .mid_top_of(ids.canvas) .set(ids.rounded_rectangle, ui); let start = [-40.0, -40.0]; @@ -190,7 +193,7 @@ pub fn gui(ui: &mut conrod_core::UiCell, ids: &Ids, app: &mut DemoApp) { let points = once(left).chain(once(top)).chain(once(right)); widget::PointPath::centred(points).right(SHAPE_GAP).set(ids.point_path, ui); - widget::Rectangle::fill([80.0, 80.0]).right(SHAPE_GAP).set(ids.rectangle_fill, ui); + widget::Rectangle::fill([80.0, 80.0]).mid_top_of(ids.canvas).set(ids.rectangle_fill, ui); widget::Rectangle::outline([80.0, 80.0]).right(SHAPE_GAP).set(ids.rectangle_outline, ui); diff --git a/backends/conrod_gfx/examples/all_winit_gfx.rs b/backends/conrod_gfx/examples/all_winit_gfx.rs index c7a1e76cd..1527b9d4f 100644 --- a/backends/conrod_gfx/examples/all_winit_gfx.rs +++ b/backends/conrod_gfx/examples/all_winit_gfx.rs @@ -119,7 +119,7 @@ fn main() { }; let dpi_factor = window.get_hidpi_factor() as f32; - + println!("get_hidpi_factor {:?}",dpi_factor); if let Some(primitives) = ui.draw_if_changed() { let dims = (win_w as f32 * dpi_factor, win_h as f32 * dpi_factor); diff --git a/backends/conrod_ggez/Cargo.toml b/backends/conrod_ggez/Cargo.toml new file mode 100644 index 000000000..295273bd0 --- /dev/null +++ b/backends/conrod_ggez/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "conrod_ggez" +version = "0.65.0" +authors = ["Mitchell Nordine "] +keywords = ["ui", "widgets", "gui", "interface", "graphics"] +description = "An easy-to-use, 100% Rust, extensible 2D GUI library." +license = "MIT OR Apache-2.0" +readme = "../../README.md" +repository = "https://github.com/pistondevelopers/conrod.git" +homepage = "https://github.com/pistondevelopers/conrod" +documentation = "https://docs.rs/conrod" +categories = ["gui"] + +[lib] +name = "conrod_ggez" +path = "./src/lib.rs" + +[dependencies] +conrod_core = { path = "../../conrod_core", version = "0.65" } +gfx = { version = "0.17" } +gfx_core = { version = "0.8" } +gfx_device_gl = "0.15" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +ggez = { git = "https://github.com/ozkriff/ggez", rev = "a23153a18" } +conrod_winit = { path = "../conrod_winit", version = "0.65" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +good-web-game = { git="https://github.com/not-fl3/good-web-game"} +stdweb = "0.4.12" + +[dev-dependencies] +conrod_example_shared = { path = "../conrod_example_shared", version = "0.65" } +find_folder = "0.3.0" +image = "0.19" +petgraph = "0.4" +## glutin_gfx.rs example dependencies \ No newline at end of file diff --git a/backends/conrod_ggez/examples/ggez.rs b/backends/conrod_ggez/examples/ggez.rs new file mode 100644 index 000000000..002fe1db8 --- /dev/null +++ b/backends/conrod_ggez/examples/ggez.rs @@ -0,0 +1,164 @@ +//! The simplest possible example that does something. +extern crate conrod_core; +extern crate conrod_example_shared; +extern crate conrod_ggez; + +extern crate gfx; +extern crate gfx_core; + +use conrod_ggez::{ggez,map_key}; +use ggez::conf::{WindowMode}; +use ggez::graphics; +use ggez::{Context, GameResult}; +use ggez::event::{self, KeyCode, KeyMods, MouseButton}; +use std::time::SystemTime; +use conrod_example_shared::{WIN_W, WIN_H}; + +use gfx::Device; + +const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.0]; + +struct MainState <'a> { + pos_x: f32, + app: conrod_example_shared::DemoApp, + ui: conrod_core::Ui, + ids: conrod_example_shared::Ids, + image_map: conrod_core::image::Map<(gfx::handle::ShaderResourceView, (u32, u32))>, + renderer: conrod_ggez::Renderer<'a> +} + +impl<'a> MainState<'a> { + fn new(ctx: &mut Context) -> GameResult { + // Create Ui and Ids of widgets to instantiate + let dpi_factor = graphics::hidpi_factor(ctx) as f64; + let mut ui = conrod_core::UiBuilder::new([WIN_W as f64, WIN_H as f64]) + .theme(conrod_example_shared::theme()) + .build(); + let ids = conrod_example_shared::Ids::new(ui.widget_id_generator()); + + // Load font from file + let assets = find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(); + let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); + ui.fonts.insert_from_file(font_path).unwrap(); + let rtv = graphics::screen_render_target(ctx); + + let factory = graphics::factory(ctx); + let mut image_map: conrod_core::image::Map<_> = conrod_core::image::Map::new(); + let rust_logo = image_map.insert(load_rust_logo::(factory)); + + // Demonstration app state that we'll control with our conrod GUI. + let app = conrod_example_shared::DemoApp::new(rust_logo); + let renderer = conrod_ggez::Renderer::new(factory, &rtv, dpi_factor).unwrap(); + let s = MainState { pos_x: 0.0,app:app,ui:ui,ids:ids,image_map:image_map,renderer:renderer}; + Ok(s) + } +} + +impl<'a> event::EventHandler for MainState<'a> { + fn update(&mut self, ctx: &mut Context) -> GameResult { + self.pos_x = self.pos_x % 800.0 + 1.0; + let mut ui = self.ui.set_widgets(); + conrod_example_shared::gui(&mut ui, &self.ids, &mut self.app); + Ok(()) + } + + fn draw(&mut self, ctx: &mut Context) -> GameResult { + let dpi_factor =graphics::hidpi_factor(ctx); + //let dpi_factor =1.16; + + if let Some(primitives) = self.ui.draw_if_changed() { + { + println!("get_hidpi_factor {:?} {:?}",dpi_factor,SystemTime::now()); + let (factory, device, mut encoder, _depthview, _colorview) = graphics::gfx_objects(ctx); + self.renderer.clear(&mut encoder, CLEAR_COLOR); + let dims = (WIN_W as f32 * dpi_factor, WIN_H as f32 * dpi_factor); + + //Clear the window + self.renderer.clear(&mut encoder, CLEAR_COLOR); + + self.renderer.fill(&mut encoder,dims,dpi_factor as f64,primitives,&self.image_map); + + self.renderer.draw(factory,&mut encoder,&self.image_map); + + } + graphics::present(ctx)?; + } + Ok(()) + } + fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) { + conrod_ggez::event::mouse_button_down_event(&mut self.ui,button); + } + + fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: f32, y: f32) { + conrod_ggez::event::mouse_button_up_event(&mut self.ui,button); + } + + fn mouse_motion_event(&mut self, _ctx: &mut Context, x: f32, y: f32, _xrel: f32, _yrel: f32) { + conrod_ggez::event::mouse_motion_event(&mut self.ui,x,y); + } + + fn mouse_wheel_event(&mut self, _ctx: &mut Context, x: f32, y: f32) { + conrod_ggez::event::mouse_wheel_event(&mut self.ui,x,y); + } + + fn key_down_event( + &mut self, + _ctx: &mut Context, + keycode: KeyCode, + keymod: KeyMods, + repeat: bool, + ) { + //conrod_ggez::event::key_down_event(&mut self.ui,keycode,keymod); + } + + fn key_up_event(&mut self, _ctx: &mut Context, keycode: KeyCode, keymod: KeyMods) { + //conrod_ggez::event::key_up_event(&mut self.ui,keycode,keymod); + } + + fn focus_event(&mut self, _ctx: &mut Context, _gained: bool) { + + } +} +// Load the Rust logo from our assets folder to use as an example image. +fn load_rust_logo>(factory: &mut F) -> (gfx::handle::ShaderResourceView::View>,(u32,u32)) { + use gfx::{format, texture}; + use gfx::memory::Bind; + use gfx::memory::Usage; + let assets = find_folder::Search::ParentsThenKids(5, 3).for_folder("assets").unwrap(); + let path = assets.join("images/rust.png"); + let rgba_image = image::open(&std::path::Path::new(&path)).unwrap().to_rgba(); + let image_dimensions = rgba_image.dimensions(); + let kind = texture::Kind::D2( + image_dimensions.0 as texture::Size, + image_dimensions.1 as texture::Size, + texture::AaMode::Single + ); + let info = texture::Info { + kind: kind, + levels: 1, + format: ::get_surface_type(), + bind: Bind::SHADER_RESOURCE, + usage: Usage::Dynamic, + }; + let raw = factory.create_texture_raw( + info, + Some(::get_channel_type()), + Some((&[rgba_image.into_raw().as_slice()], texture::Mipmap::Provided))).unwrap(); + let tex = gfx_core::memory::Typed::new(raw); + let view = factory.view_texture_as_shader_resource::( + &tex, (0,0), format::Swizzle::new() + ).unwrap(); + (view,image_dimensions) +} +pub fn main() -> GameResult { + println!("w {:?}, h {:?}",WIN_W,WIN_H); + let cb = ggez::ContextBuilder::new("super_simple", "ggez") + .window_mode( + WindowMode::default() + .dimensions(WIN_W as f32, WIN_H as f32) + .resizable(true), + ); + let (ctx, event_loop) = &mut cb.build()?; + let state = &mut MainState::new(ctx)?; + event::run(ctx, event_loop, state) +} diff --git a/backends/conrod_ggez/src/ggez_event.rs b/backends/conrod_ggez/src/ggez_event.rs new file mode 100644 index 000000000..1e23dce32 --- /dev/null +++ b/backends/conrod_ggez/src/ggez_event.rs @@ -0,0 +1,47 @@ +use ggez::input::keyboard::{KeyCode,KeyMods}; +use ggez::input::mouse::{MouseButton::{self,Left,Right,Middle,Other}}; +use conrod_winit::map_key; +use conrod_core::event::Input; +use conrod_core::input::Button::Keyboard; +use conrod_core::input::Button::Mouse; +use conrod_core::input::Motion; +use conrod_core::Ui; + +pub fn mouse_button_down_event(ui:&mut Ui,button: MouseButton){ + let mmb =match button{ + Left=> conrod_core::input::state::mouse::Button::Left, + Right=> conrod_core::input::state::mouse::Button::Right, + Middle=> conrod_core::input::state::mouse::Button::Middle, + Other(_j) => conrod_core::input::state::mouse::Button::Unknown + }; + ui.handle_event(Input::Press(Mouse(mmb))); +} +pub fn mouse_button_up_event(ui:&mut Ui,button: MouseButton){ + let mmb =match button{ + Left=> conrod_core::input::state::mouse::Button::Left, + Right=> conrod_core::input::state::mouse::Button::Right, + Middle=> conrod_core::input::state::mouse::Button::Middle, + Other(_j) => conrod_core::input::state::mouse::Button::Unknown + }; + ui.handle_event(Input::Release(Mouse(mmb))); +} +pub fn mouse_motion_event(ui:&mut Ui,xx: f32, yy: f32){ + let x = xx as f64; + let y = yy as f64; + ui.handle_event(Input::Motion(Motion::MouseCursor{x,y})); +} +pub fn mouse_wheel_event(ui:&mut Ui,xx: f32, yy: f32){ + let x = xx as f64; + let y = yy as f64; + ui.handle_event(Input::Motion(Motion::Scroll{x,y})); +} +pub fn key_down_event(ui:&mut Ui,key:KeyCode,_keymod:KeyMods){ + ui.handle_event(Input::Press(Keyboard(map_key(key.clone())))); + ui.handle_event(Input::Press(Keyboard(map_key(key.clone())))); +} +pub fn key_up_event(ui:&mut Ui,key:KeyCode,_keymod:KeyMods){ + ui.handle_event(Input::Release(Keyboard(map_key(key.clone())))); +} +pub fn text_input_event(ui:&mut Ui,ch: char){ + ui.handle_event(Input::Text(ch.to_string())); +} \ No newline at end of file diff --git a/backends/conrod_ggez/src/gwg_event.rs b/backends/conrod_ggez/src/gwg_event.rs new file mode 100644 index 000000000..132f94071 --- /dev/null +++ b/backends/conrod_ggez/src/gwg_event.rs @@ -0,0 +1,50 @@ +use conrod_core::input; +use conrod_core::event::Input; +use conrod_core::input::Button::Keyboard; +use conrod_core::input::Button::Mouse; +use conrod_core::input::Motion; +use conrod_core::Ui; +use stdweb::web::event::MouseButton; +pub fn map_key(keycode: String) -> input::keyboard::Key { + input::keyboard::Key::Hash +} + +pub fn mouse_button_down_event(ui:&mut Ui,button: MouseButton){ + let mmb =match button{ + Left=> conrod_core::input::state::mouse::Button::Left, + Right=> conrod_core::input::state::mouse::Button::Right, + Wheel=> conrod_core::input::state::mouse::Button::Middle, + Button4 => conrod_core::input::state::mouse::Button::Unknown, + Button5 => conrod_core::input::state::mouse::Button::Unknown + }; + ui.handle_event(Input::Press(Mouse(mmb))); +} +pub fn mouse_button_up_event(ui:&mut Ui,button: MouseButton){ + let mmb =match button{ + Left=> conrod_core::input::state::mouse::Button::Left, + Right=> conrod_core::input::state::mouse::Button::Right, + Wheel=> conrod_core::input::state::mouse::Button::Middle, + Button4 => conrod_core::input::state::mouse::Button::Unknown, + Button5 => conrod_core::input::state::mouse::Button::Unknown + }; + ui.handle_event(Input::Release(Mouse(mmb))); +} +pub fn mouse_motion_event(ui:&mut Ui,xx: f32, yy: f32){ + let x = xx as f64; + let y = yy as f64; + ui.handle_event(Input::Motion(Motion::MouseCursor{x,y})); +} +pub fn mouse_wheel_event(ui:&mut Ui,xx: f32, yy: f32){ + let x = xx as f64; + let y = yy as f64; + ui.handle_event(Input::Motion(Motion::Scroll{x,y})); +} +pub fn key_down_event(ui:&mut Ui,key:String){ + //ui.handle_event(Input::Press(Keyboard(map_key(key.clone())))); +} +pub fn key_up_event(ui:&mut Ui,key:String){ + //ui.handle_event(Input::Release(Keyboard(map_key(key.clone())))); +} +pub fn text_input_event(ui:&mut Ui,ch: char){ + ui.handle_event(Input::Text(ch.to_string())); +} \ No newline at end of file diff --git a/backends/conrod_ggez/src/lib.rs b/backends/conrod_ggez/src/lib.rs new file mode 100644 index 000000000..9d3b7b305 --- /dev/null +++ b/backends/conrod_ggez/src/lib.rs @@ -0,0 +1,759 @@ +//! A gfx backend for rendering conrod primitives. +extern crate conrod_core; +#[macro_use] +extern crate gfx; +extern crate gfx_core; +extern crate gfx_device_gl; +#[cfg(not(target_arch = "wasm32"))] +extern crate conrod_winit; +#[cfg(not(target_arch = "wasm32"))] +pub extern crate ggez; +#[cfg(not(target_arch = "wasm32"))] +pub use conrod_winit::map_key; +#[cfg(not(target_arch = "wasm32"))] +pub mod ggez_event; +#[cfg(not(target_arch = "wasm32"))] +pub use ggez_event as event; + +#[cfg(target_arch = "wasm32")] +pub extern crate good_web_game as ggez; +#[cfg(target_arch = "wasm32")] +pub use conrod_winit::map_key; +#[cfg(target_arch = "wasm32")] +pub mod gwg_event; +#[cfg(target_arch = "wasm32")] +pub use gwg_event as event; +#[cfg(target_arch = "wasm32")] +extern crate stdweb; + +use gfx::{Factory, texture, PipelineState, + handle::RawRenderTargetView, + traits::FactoryExt, +}; + +use conrod_core::{ + Rect, + Scalar, + color, + image, + render, + text::{rt, GlyphCache}, +}; + +/// A `Command` describing a step in the drawing process. +#[derive(Clone, Debug)] +pub enum Command<'a> { + /// Draw to the target. + Draw(Draw<'a>), + /// Update the scizzor within the pipeline. + Scizzor(gfx::Rect), +} + +/// An iterator yielding `Command`s, produced by the `Renderer::commands` method. +pub struct Commands<'a> { + commands: std::slice::Iter<'a, PreparedCommand>, + vertices: &'a [Vertex], +} + +/// A `Command` for drawing to the target. +/// +/// Each variant describes how to draw the contents of the vertex buffer. +#[derive(Clone, Debug)] +pub enum Draw<'a> { + /// A range of vertices representing triangles textured with the image in the + /// image_map at the given `widget::Id`. + Image(image::Id, &'a [Vertex]), + /// A range of vertices representing plain triangles. + Plain(&'a [Vertex]), +} + +enum PreparedCommand { + Image(image::Id, std::ops::Range), + Plain(std::ops::Range), + Scizzor(gfx::Rect), +} + +/// Draw text from the text cache texture `tex` in the fragment shader. +pub const MODE_TEXT: u32 = 0; +/// Draw an image from the texture at `tex` in the fragment shader. +pub const MODE_IMAGE: u32 = 1; +/// Ignore `tex` and draw simple, colored 2D geometry. +pub const MODE_GEOMETRY: u32 = 2; + +const FRAGMENT_SHADER: &'static [u8] = b" + #version 140 + uniform sampler2D t_Color; + + in vec2 v_Uv; + in vec4 v_Color; + flat in uint v_Mode; + + out vec4 f_Color; + + void main() { + // Text + if (v_Mode == uint(0)) { + f_Color = v_Color * vec4(1.0, 1.0, 1.0, texture(t_Color, v_Uv).a); + + // Image + } else if (v_Mode == uint(1)) { + f_Color = texture(t_Color, v_Uv); + + // 2D Geometry + } else if (v_Mode == uint(2)) { + f_Color = v_Color; + } + } +"; + +const VERTEX_SHADER: &'static [u8] = b" + #version 140 + + in vec2 a_Pos; + in vec2 a_Uv; + in vec4 a_Color; + in uint a_Mode; + + out vec2 v_Uv; + out vec4 v_Color; + flat out uint v_Mode; + + void main() { + v_Uv = a_Uv; + v_Color = a_Color; + gl_Position = vec4(a_Pos, 0.0, 1.0); + v_Mode = a_Mode; + } +"; + +/// Possible errors that may occur during a call to `Renderer::new`. +#[derive(Debug)] +pub enum RendererCreationError { + /// Errors that might occur when creating the pipeline. + PipelineState(gfx::PipelineStateError), +} + +// Format definitions (must be pub for gfx_defines to use them) +/// Color format used with gfx buffers. +pub type ColorFormat = gfx::format::Srgba8; +type SurfaceFormat = gfx::format::R8_G8_B8_A8; +type FullFormat = (SurfaceFormat, gfx::format::Unorm); + +// This is it's own module to allow_unsafe within it +mod defines { + //it appears gfx_defines generates unsafe code + #![allow(unsafe_code)] + + use gfx; + use super::ColorFormat; + // Vertex and pipeline declarations + gfx_defines! { + vertex Vertex { + pos: [f32; 2] = "a_Pos", + uv: [f32; 2] = "a_Uv", + color: [f32; 4] = "a_Color", + mode: u32 = "a_Mode", + } + + pipeline pipe { + vbuf: gfx::VertexBuffer = (), + color: gfx::TextureSampler<[f32; 4]> = "t_Color", + scissor: gfx::Scissor = (), + //out: gfx::RenderTarget = "Target0", + out: gfx::BlendTarget = ("f_Color", ::gfx::state::ColorMask::all(), ::gfx::preset::blend::ALPHA), + } + + } +} + +use self::defines::*; + +/// This type is used for translating `render::Primitives` into `Commands`s that indicate how to +/// draw the GUI using `gfx`. +pub struct Renderer<'a> { + pipeline: PipelineState, + glyph_cache: GlyphCache<'a>, + cache_tex: gfx::handle::Texture, + cache_tex_view: gfx::handle::ShaderResourceView, + data: pipe::Data, + commands: Vec, + vertices: Vec, +} + +impl<'a> Renderer<'a> { + /// Create a new renderer from a `gfx::Factory`, `gfx::handle::RawRenderTargetView` and + /// a given `dpi_factor` + pub fn new(factory: &mut F, + rtv: &RawRenderTargetView, + dpi_factor: f64) + -> Result + where F: Factory, + { + let sampler_info = texture::SamplerInfo::new( + texture::FilterMethod::Bilinear, + texture::WrapMode::Clamp, + ); + let sampler = factory.create_sampler(sampler_info); + + let vbuf = factory.create_vertex_buffer(&[]); + let (_, fake_texture) = create_texture(factory, 1, 1, &[0; 4]); + + let (width, height, _depth, _samples) = rtv.get_dimensions(); + + let data = pipe::Data { + vbuf, + scissor: gfx::Rect { x: 0, y: 0, w: width, h: height }, + color: (fake_texture.clone(), sampler), + out: gfx::memory::Typed::new(rtv.clone()), + }; + + let shader_set = factory.create_shader_set(VERTEX_SHADER, FRAGMENT_SHADER).unwrap(); + + let pipeline = factory.create_pipeline_state( + &shader_set, + gfx::Primitive::TriangleList, + gfx::state::Rasterizer { + samples: Some(gfx::state::MultiSample {}), + ..gfx::state::Rasterizer::new_fill() + }, + pipe::new())?; + + let (glyph_cache, cache_tex, cache_tex_view) = { + let width = (width as f64 * dpi_factor) as u32; + let height = (height as f64 * dpi_factor) as u32; + + const SCALE_TOLERANCE: f32 = 0.1; + const POSITION_TOLERANCE: f32 = 0.1; + + let cache = GlyphCache::builder() + .dimensions(width, height) + .scale_tolerance(SCALE_TOLERANCE) + .position_tolerance(POSITION_TOLERANCE) + .build(); + + let data = vec![0; (width * height * 4) as usize]; + + let (texture, texture_view) = create_texture(factory, width, height, &data); + + (cache, texture, texture_view) + }; + Ok(Renderer { + pipeline, + glyph_cache, + cache_tex, + cache_tex_view, + data, + commands: vec![], + vertices: vec![], + }) + } + + /// Produce an `Iterator` yielding `Command`s. + pub fn commands(&self) -> Commands { + let Renderer { ref commands, ref vertices, .. } = *self; + Commands { + commands: commands.iter(), + vertices: vertices, + } + } + + /// Fill the inner vertex and command buffers by translating the given `primitives`. + pub fn fill(&mut self, + encoder: &mut gfx::Encoder, + dims: (f32, f32), + dpi_factor: f64, + mut primitives: P, + image_map: &image::Map<(gfx::handle::ShaderResourceView, + (u32, u32))>) + where P: render::PrimitiveWalker, + C: gfx::CommandBuffer, + { + let Renderer { ref mut commands, ref mut vertices, ref mut glyph_cache, ref mut cache_tex, .. } = *self; + + commands.clear(); + vertices.clear(); + + enum State { + Image { image_id: image::Id, start: usize }, + Plain { start: usize }, + } + + let mut current_state = State::Plain { start: 0 }; + + // Switches to the `Plain` state and completes the previous `Command` if not already in the + // `Plain` state. + macro_rules! switch_to_plain_state { + () => { + match current_state { + State::Plain { .. } => (), + State::Image { image_id, start } => { + commands.push(PreparedCommand::Image(image_id, start..vertices.len())); + current_state = State::Plain { start: vertices.len() }; + }, + } + }; + } + + // Framebuffer dimensions and the "dots per inch" factor. + let (screen_w, screen_h) = dims; + let (win_w, win_h) = (screen_w as Scalar, screen_h as Scalar); + let half_win_w = win_w / 2.0; + let half_win_h = win_h / 2.0; + + // Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0). + let vx = |x: Scalar| (x * dpi_factor / half_win_w) as f32; + let vy = |y: Scalar| (y * dpi_factor / half_win_h) as f32; + + let mut current_scizzor = gfx::Rect { + x: 0, + w: screen_w as u16, + y: 0, + h: screen_h as u16, + }; + + let rect_to_gfx_rect = |rect: Rect| { + let (w, h) = rect.w_h(); + let left = (rect.left() * dpi_factor + half_win_w) as u16; + let bottom = (rect.bottom() * dpi_factor + half_win_h) as u16; + let width = (w * dpi_factor) as u16; + let height = (h * dpi_factor) as u16; + gfx::Rect { + x: std::cmp::max(left, 0), + w: std::cmp::min(width, screen_w as u16), + y: std::cmp::max(bottom, 0), + h: std::cmp::min(height, screen_h as u16), + } + }; + + // Draw each primitive in order of depth. + while let Some(primitive) = primitives.next_primitive() { + let render::Primitive { kind, scizzor, rect, .. } = primitive; + + // Check for a `Scizzor` command. + let new_scizzor = rect_to_gfx_rect(scizzor); + if new_scizzor != current_scizzor { + // Finish the current command. + match current_state { + State::Plain { start } => + commands.push(PreparedCommand::Plain(start..vertices.len())), + State::Image { image_id, start } => + commands.push(PreparedCommand::Image(image_id, start..vertices.len())), + } + + // Update the scizzor and produce a command. + current_scizzor = new_scizzor; + commands.push(PreparedCommand::Scizzor(new_scizzor)); + + // Set the state back to plain drawing. + current_state = State::Plain { start: vertices.len() }; + } + + match kind { + render::PrimitiveKind::Rectangle { color } => { + switch_to_plain_state!(); + + let color = gamma_srgb_to_linear(color.to_fsa()); + let (l, r, b, t) = rect.l_r_b_t(); + + let v = |x, y| { + // Convert from conrod Scalar range to GL range -1.0 to 1.0. + Vertex { + pos: [vx(x), vy(y)], + uv: [0.0, 0.0], + color: color, + mode: MODE_GEOMETRY, + } + }; + + let mut push_v = |x, y| vertices.push(v(x, y)); + + // Bottom left triangle. + push_v(l, t); + push_v(r, b); + push_v(l, b); + + // Top right triangle. + push_v(l, t); + push_v(r, b); + push_v(r, t); + }, + _ =>{} +/* + render::PrimitiveKind::TrianglesSingleColor { color, triangles } => { + if triangles.is_empty() { + continue; + } + + switch_to_plain_state!(); + + let color = gamma_srgb_to_linear(color.into()); + + let v = |p: [Scalar; 2]| { + Vertex { + pos: [vx(p[0]), vy(p[1])], + uv: [0.0, 0.0], + color: color, + mode: MODE_GEOMETRY, + } + }; + + for triangle in triangles { + vertices.push(v(triangle[0])); + vertices.push(v(triangle[1])); + vertices.push(v(triangle[2])); + } + } + + render::PrimitiveKind::TrianglesMultiColor { triangles } => { + if triangles.is_empty() { + continue; + } + + switch_to_plain_state!(); + + let v = |(p, c): ([Scalar; 2], color::Rgba)| { + Vertex { + pos: [vx(p[0]), vy(p[1])], + uv: [0.0, 0.0], + color: gamma_srgb_to_linear(c.into()), + mode: MODE_GEOMETRY, + } + }; + + for triangle in triangles { + vertices.push(v(triangle[0])); + vertices.push(v(triangle[1])); + vertices.push(v(triangle[2])); + } + } + + render::PrimitiveKind::Text { color, text, font_id } => { + switch_to_plain_state!(); + + let positioned_glyphs = text.positioned_glyphs(dpi_factor as f32); + + // Queue the glyphs to be cached + for glyph in positioned_glyphs { + glyph_cache.queue_glyph(font_id.index(), glyph.clone()); + } + + glyph_cache.cache_queued(|rect, data| { + let offset = [rect.min.x as u16, rect.min.y as u16]; + let size = [rect.width() as u16, rect.height() as u16]; + + let new_data = data.iter().map(|x| [255, 255, 255, *x]).collect::>(); + + update_texture(encoder, &cache_tex, offset, size, &new_data); + }).unwrap(); + + let color = gamma_srgb_to_linear(color.to_fsa()); + let cache_id = font_id.index(); + let origin = rt::point(0.0, 0.0); + + // A closure to convert RustType rects to GL rects + let to_gl_rect = |screen_rect: rt::Rect| rt::Rect { + min: origin + + (rt::vector(screen_rect.min.x as f32 / screen_w - 0.5, + 1.0 - screen_rect.min.y as f32 / screen_h - 0.5)) * 2.0, + max: origin + + (rt::vector(screen_rect.max.x as f32 / screen_w - 0.5, + 1.0 - screen_rect.max.y as f32 / screen_h - 0.5)) * 2.0, + }; + + for g in positioned_glyphs { + if let Ok(Some((uv_rect, screen_rect))) = glyph_cache.rect_for(cache_id, g) { + let gl_rect = to_gl_rect(screen_rect); + let v = |p, t| Vertex { + pos: p, + uv: t, + color: color, + mode: MODE_TEXT, + }; + let mut push_v = |p, t| vertices.push(v(p, t)); + push_v([gl_rect.min.x, gl_rect.max.y], [uv_rect.min.x, uv_rect.max.y]); + push_v([gl_rect.min.x, gl_rect.min.y], [uv_rect.min.x, uv_rect.min.y]); + push_v([gl_rect.max.x, gl_rect.min.y], [uv_rect.max.x, uv_rect.min.y]); + push_v([gl_rect.max.x, gl_rect.min.y], [uv_rect.max.x, uv_rect.min.y]); + push_v([gl_rect.max.x, gl_rect.max.y], [uv_rect.max.x, uv_rect.max.y]); + push_v([gl_rect.min.x, gl_rect.max.y], [uv_rect.min.x, uv_rect.max.y]); + } + } + } + + render::PrimitiveKind::Image { image_id, color, source_rect } => { + + // Switch to the `Image` state for this image if we're not in it already. + let new_image_id = image_id; + match current_state { + + // If we're already in the drawing mode for this image, we're done. + State::Image { image_id, .. } if image_id == new_image_id => (), + + // If we were in the `Plain` drawing state, switch to Image drawing state. + State::Plain { start } => { + commands.push(PreparedCommand::Plain(start..vertices.len())); + current_state = State::Image { + image_id: new_image_id, + start: vertices.len(), + }; + } + + // If we were drawing a different image, switch state to draw *this* image. + State::Image { image_id, start } => { + commands.push(PreparedCommand::Image(image_id, start..vertices.len())); + current_state = State::Image { + image_id: new_image_id, + start: vertices.len(), + }; + } + } + + let color = color.unwrap_or(color::WHITE).to_fsa(); + + let (image_w, image_h) = image_map.get(&image_id).unwrap().1; + let (image_w, image_h) = (image_w as Scalar, image_h as Scalar); + + // Get the sides of the source rectangle as uv coordinates. + // + // Texture coordinates range: + // - left to right: 0.0 to 1.0 + // - bottom to top: 1.0 to 0.0 + // Note bottom and top are flipped in comparison to glium so that we don't need to flip images when loading + let (uv_l, uv_r, uv_t, uv_b) = match source_rect { + Some(src_rect) => { + let (l, r, b, t) = src_rect.l_r_b_t(); + ((l / image_w) as f32, + (r / image_w) as f32, + (b / image_h) as f32, + (t / image_h) as f32) + } + None => (0.0, 1.0, 0.0, 1.0), + }; + + let v = |x, y, t| { + // Convert from conrod Scalar range to GL range -1.0 to 1.0. + let x = (x * dpi_factor as Scalar / half_win_w) as f32; + let y = (y * dpi_factor as Scalar / half_win_h) as f32; + Vertex { + pos: [x, y], + uv: t, + color: color, + mode: MODE_IMAGE, + } + }; + + let mut push_v = |x, y, t| vertices.push(v(x, y, t)); + + let (l, r, b, t) = rect.l_r_b_t(); + + // Bottom left triangle. + push_v(l, t, [uv_l, uv_t]); + push_v(r, b, [uv_r, uv_b]); + push_v(l, b, [uv_l, uv_b]); + + // Top right triangle. + push_v(l, t, [uv_l, uv_t]); + push_v(r, b, [uv_r, uv_b]); + push_v(r, t, [uv_r, uv_t]); + } + + // We have no special case widgets to handle. + render::PrimitiveKind::Other(_) => (), + */ + } + + } + + // Enter the final command. + match current_state { + State::Plain { start } => + commands.push(PreparedCommand::Plain(start..vertices.len())), + State::Image { image_id, start } => + commands.push(PreparedCommand::Image(image_id, start..vertices.len())), + } + } + + /// Draws using the inner list of `Command`s to the given `display`. + /// + /// Note: If you require more granular control over rendering, you may want to use the `fill` + /// and `commands` methods separately. This method is simply a convenience wrapper around those + /// methods for the case that the user does not require accessing or modifying conrod's draw + /// parameters, uniforms or generated draw commands. + pub fn draw(&self, factory: &mut F, encoder: &mut gfx::Encoder, image_map: &image::Map<(gfx::handle::ShaderResourceView, (u32, u32))>) + where F: Factory, + C: gfx::CommandBuffer, + { + let Renderer { ref pipeline, ref data, ref cache_tex_view, .. } = *self; + + let mut data = data.clone(); + + for command in self.commands() { + match command { + + // Update the `scizzor` before continuing to draw. + Command::Scizzor(scizzor) => data.scissor = scizzor, + + // Draw to the target with the given `draw` command. + Command::Draw(draw) => match draw { + + // Draw text and plain 2D geometry. + Draw::Plain(verts) => { + data.color.0 = cache_tex_view.clone(); + let (vbuf, slice) = factory.create_vertex_buffer_with_slice(&verts, ()); + data.vbuf = vbuf; + encoder.draw(&slice, &pipeline, &data); + } + + // Draw an image whose texture data lies within the `image_map` at the + // given `id`. + Draw::Image(image_id, verts) => { + let image = &image_map.get(&image_id).unwrap().0; + data.color.0 = image.clone(); + let (vbuf, slice) = factory.create_vertex_buffer_with_slice(&verts, ()); + data.vbuf = vbuf; + encoder.draw(&slice, &pipeline, &data); + } + } + } + } + } + + /// Call this routine when a window has been resized. This ensures that conrod primitives are + /// drawn properly with the `draw` call. + pub fn on_resize(&mut self, rtv: RawRenderTargetView) { + let (width, height, _depth, _samples) = rtv.get_dimensions(); + self.data.out = gfx::memory::Typed::new(rtv.clone()); + self.data.scissor = gfx::Rect { x: 0, y: 0, w: width, h: height }; + } + + /// Call this routine to clear the render target. + pub fn clear(&self, encoder: &mut gfx::Encoder, clear_color: [f32; 4]) + where C: gfx::CommandBuffer, + { + encoder.clear(&self.data.out, clear_color); + } +} + +fn gamma_srgb_to_linear(c: [f32; 4]) -> [f32; 4] { + fn component(f: f32) -> f32 { + // Taken from https://github.com/PistonDevelopers/graphics/src/color.rs#L42 + if f <= 0.04045 { + f / 12.92 + } else { + ((f + 0.055) / 1.055).powf(2.4) + } + } + [component(c[0]), component(c[1]), component(c[2]), c[3]] +} + +// Creates a gfx texture with the given data +fn create_texture(factory: &mut F, width: u32, height: u32, data: &[u8]) + -> (gfx::handle::Texture, gfx::handle::ShaderResourceView) + where F: gfx::Factory +{ + // Modified `Factory::create_texture_immutable_u8` for dynamic texture. + fn create_texture( + factory: &mut F, + kind: gfx::texture::Kind, + data: &[&[u8]], + ) -> Result<( + gfx::handle::Texture, + gfx::handle::ShaderResourceView + ), gfx::CombinedError> + where F: gfx::Factory, + T: gfx::format::TextureFormat + { + use gfx::{format, texture}; + use gfx::memory::Usage; + use gfx::memory::Bind; + + use gfx_core::memory::Typed; + + let surface = ::get_surface_type(); + let num_slices = kind.get_num_slices().unwrap_or(1) as usize; + let num_faces = if kind.is_cube() { 6 } else { 1 }; + let desc = texture::Info { + kind: kind, + levels: (data.len() / (num_slices * num_faces)) as texture::Level, + format: surface, + bind: Bind::SHADER_RESOURCE, + usage: Usage::Dynamic, + }; + let cty = ::get_channel_type(); + let raw = factory.create_texture_raw( + desc, + Some(cty), + Some((data, gfx::texture::Mipmap::Provided)))?; + let levels = (0, raw.get_info().levels - 1); + let tex = Typed::new(raw); + let view = factory.view_texture_as_shader_resource::( + &tex, levels, format::Swizzle::new(), + )?; + Ok((tex, view)) + } + + let kind = texture::Kind::D2( + width as texture::Size, + height as texture::Size, + texture::AaMode::Single, + ); + create_texture::(factory, kind, &[data]).unwrap() +} + +// Updates a texture with the given data (used for updating the GlyphCache texture) +fn update_texture(encoder: &mut gfx::Encoder, + texture: &gfx::handle::Texture, + offset: [u16; 2], + size: [u16; 2], + data: &[[u8; 4]]) + where C: gfx::CommandBuffer +{ + let info = texture::ImageInfoCommon { + xoffset: offset[0], + yoffset: offset[1], + zoffset: 0, + width: size[0], + height: size[1], + depth: 0, + format: (), + mipmap: 0, + }; + + encoder.update_texture::(texture, None, info, data).unwrap(); +} + +impl<'a> Iterator for Commands<'a> { + type Item = Command<'a>; + fn next(&mut self) -> Option { + let Commands { ref mut commands, ref vertices } = *self; + commands.next().map(|command| match *command { + PreparedCommand::Scizzor(scizzor) => Command::Scizzor(scizzor), + PreparedCommand::Plain(ref range) => + Command::Draw(Draw::Plain(&vertices[range.clone()])), + PreparedCommand::Image(id, ref range) => + Command::Draw(Draw::Image(id, &vertices[range.clone()])), + }) + } +} + +impl From> for RendererCreationError { + fn from(err: gfx::PipelineStateError) -> Self { + RendererCreationError::PipelineState(err) + } +} + +impl std::error::Error for RendererCreationError { + fn description(&self) -> &str { + match *self { + RendererCreationError::PipelineState(ref e) => std::error::Error::description(e), + } + } +} + +impl std::fmt::Display for RendererCreationError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + match *self { + RendererCreationError::PipelineState(ref e) => std::fmt::Display::fmt(e, f), + } + } +} diff --git a/backends/conrod_glium/examples/hello_world.rs b/backends/conrod_glium/examples/hello_world.rs index 23e623011..d7609eb23 100644 --- a/backends/conrod_glium/examples/hello_world.rs +++ b/backends/conrod_glium/examples/hello_world.rs @@ -9,11 +9,11 @@ extern crate glium; mod support; -use conrod_core::{widget, Colorable, Positionable, Widget}; +use conrod_core::{widget, Colorable, Positionable, Widget,color,Sizeable}; use glium::Surface; -const WIDTH: u32 = 400; -const HEIGHT: u32 = 200; +const WIDTH: u32 = 600; +const HEIGHT: u32 = 420; fn main() { // Build the window. @@ -36,7 +36,7 @@ fn main() { // Add a `Font` to the `Ui`'s `font::Map` from file. let assets = find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(); - let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); + let font_path = assets.join("crayon/assets/Oswald-Heavy.ttf"); ui.fonts.insert_from_file(font_path).unwrap(); // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used @@ -96,10 +96,10 @@ fn main() { let ui = &mut ui.set_widgets(); // "Hello World!" in the middle of the screen. - widget::Text::new("Hello World!") - .middle_of(ui.window) - .color(conrod_core::color::WHITE) - .font_size(32) + widget::Text::new("H") + .color(color::LIGHT_GREEN) + .w_h(200.0,50.0) + .middle() .set(ids.text, ui); } diff --git a/backends/conrod_glium/examples/image.rs b/backends/conrod_glium/examples/image.rs index 3af8f3670..0b684d303 100644 --- a/backends/conrod_glium/examples/image.rs +++ b/backends/conrod_glium/examples/image.rs @@ -80,7 +80,7 @@ fn main() { { let ui = &mut ui.set_widgets(); // Draw a light blue background. - widget::Canvas::new().color(color::LIGHT_BLUE).set(ids.background, ui); + //widget::Canvas::new().color(color::LIGHT_BLUE).set(ids.background, ui); // Instantiate the `Image` at its full size in the middle of the window. widget::Image::new(rust_logo).w_h(w as f64, h as f64).middle().set(ids.rust_logo, ui); } @@ -99,7 +99,7 @@ fn main() { // Load the Rust logo from our assets folder to use as an example image. fn load_rust_logo(display: &glium::Display) -> glium::texture::Texture2d { let assets = find_folder::Search::ParentsThenKids(5, 3).for_folder("assets").unwrap(); - let path = assets.join("images/rust.png"); + let path = assets.join("images/crate.bmp"); let rgba_image = image::open(&std::path::Path::new(&path)).unwrap().to_rgba(); let image_dimensions = rgba_image.dimensions(); let raw_image = glium::texture::RawImage2d::from_raw_rgba_reversed(&rgba_image.into_raw(), image_dimensions); diff --git a/backends/conrod_glium/examples/rect.rs b/backends/conrod_glium/examples/rect.rs new file mode 100644 index 000000000..49378c553 --- /dev/null +++ b/backends/conrod_glium/examples/rect.rs @@ -0,0 +1,98 @@ +//! A simple example demonstrating the `Triangles` widget. + +#[macro_use] +extern crate conrod_core; +extern crate glium; +extern crate conrod_glium; +extern crate conrod_winit; +extern crate find_folder; + +mod support; + +use conrod_core::{color, widget, Widget}; +use conrod_core::widget::triangles::Triangle; +use conrod_core::Positionable; +use glium::Surface; + +fn main() { + const WIDTH: u32 = 700; + const HEIGHT: u32 = 400; + + // Build the window. + let mut events_loop = glium::glutin::EventsLoop::new(); + let window = glium::glutin::WindowBuilder::new() + .with_title("Triangles!") + .with_dimensions((WIDTH, HEIGHT).into()); + let context = glium::glutin::ContextBuilder::new() + .with_vsync(true) + .with_multisampling(4); + let display = glium::Display::new(window, context, &events_loop).unwrap(); + let display = support::GliumDisplayWinitWrapper(display); + + // construct our `Ui`. + let mut ui = conrod_core::UiBuilder::new([WIDTH as f64, HEIGHT as f64]).build(); + + // Generate the widget identifiers. + widget_ids!(struct Ids { triangles }); + let ids = Ids::new(ui.widget_id_generator()); + + // Add a `Font` to the `Ui`'s `font::Map` from file. + let assets = find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(); + let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf"); + ui.fonts.insert_from_file(font_path).unwrap(); + + // A type used for converting `conrod_core::render::Primitives` into `Command`s that can be used + // for drawing to the glium `Surface`. + let mut renderer = conrod_glium::Renderer::new(&display.0).unwrap(); + + // The image map describing each of our widget->image mappings (in our case, none). + let image_map = conrod_core::image::Map::::new(); + + events_loop.run_forever(|event| { + match event.clone() { + glium::glutin::Event::WindowEvent { event, .. } => match event { + + // Break from the loop upon `Escape` or closed window. + glium::glutin::WindowEvent::CloseRequested | + glium::glutin::WindowEvent::KeyboardInput { + input: glium::glutin::KeyboardInput { + virtual_keycode: Some(glium::glutin::VirtualKeyCode::Escape), + .. + }, + .. + } => return glium::glutin::ControlFlow::Break, + + _ => (), + }, + _ => (), + } + + // Use the `winit` backend feature to convert the winit event to a conrod one. + let input = match conrod_winit::convert_event(event, &display) { + None => return glium::glutin::ControlFlow::Continue, + Some(input) => input, + }; + + // Handle the input with the `Ui`. + ui.handle_event(input); + + // Set the triangle widget. + { + let ui = &mut ui.set_widgets(); + widget::Rectangle::fill([80.0, 80.0]) + .middle() + .set(ids.triangles, ui); + } + + // Draw the `Ui` if it has changed. + if let Some(primitives) = ui.draw_if_changed() { + renderer.fill(&display.0, primitives, &image_map); + let mut target = display.0.draw(); + target.clear_color(0.0, 0.0, 0.0, 1.0); + renderer.draw(&display.0, &mut target, &image_map).unwrap(); + target.finish().unwrap(); + } + + glium::glutin::ControlFlow::Continue + }); +} diff --git a/backends/conrod_glium/examples/text_edit.rs b/backends/conrod_glium/examples/text_edit.rs index de2d6e419..0d82ac2f5 100644 --- a/backends/conrod_glium/examples/text_edit.rs +++ b/backends/conrod_glium/examples/text_edit.rs @@ -110,7 +110,7 @@ fn set_ui(ref mut ui: conrod_core::UiCell, ids: &Ids, demo_text: &mut String) { widget::Canvas::new() .scroll_kids_vertically() - .color(color::DARK_CHARCOAL) + .color(color::BLUE) .set(ids.canvas, ui); for edit in widget::TextEdit::new(demo_text) diff --git a/backends/conrod_glium/src/lib.rs b/backends/conrod_glium/src/lib.rs index 46027aad5..e6f64ea63 100644 --- a/backends/conrod_glium/src/lib.rs +++ b/backends/conrod_glium/src/lib.rs @@ -542,7 +542,6 @@ impl Renderer { height: std::cmp::min(height, screen_h), } }; - // Draw each primitive in order of depth. while let Some(primitive) = primitives.next_primitive() { let render::Primitive { kind, scizzor, rect, .. } = primitive; @@ -614,7 +613,7 @@ impl Renderer { mode: MODE_GEOMETRY, } }; - + for triangle in triangles { vertices.push(v(triangle[0])); vertices.push(v(triangle[1])); @@ -684,7 +683,6 @@ impl Renderer { // The text cache is only ever created with U8 or U8U8U8 formats. _ => unreachable!(), }; - let image = glium::texture::RawImage2d { data: data, width: w, @@ -707,7 +705,7 @@ impl Renderer { + (text::rt::vector(screen_rect.max.x as f32 / screen_w as f32 - 0.5, 1.0 - screen_rect.max.y as f32 / screen_h as f32 - 0.5)) * 2.0 }; - + let mut l =0; for g in positioned_glyphs { if let Ok(Some((uv_rect, screen_rect))) = cache.rect_for(cache_id, g) { let gl_rect = to_gl_rect(screen_rect); @@ -724,8 +722,10 @@ impl Renderer { push_v([gl_rect.max.x, gl_rect.min.y], [uv_rect.max.x, uv_rect.min.y]); push_v([gl_rect.max.x, gl_rect.max.y], [uv_rect.max.x, uv_rect.max.y]); push_v([gl_rect.min.x, gl_rect.max.y], [uv_rect.min.x, uv_rect.max.y]); + l = l+1; } } + }, render::PrimitiveKind::Image { image_id, color, source_rect } => { @@ -858,7 +858,10 @@ impl Renderer { // Only submit the vertices if there is enough for at least one triangle. Draw::Plain(slice) => if slice.len() >= NUM_VERTICES_IN_TRIANGLE { let vertex_buffer = glium::VertexBuffer::new(facade, slice)?; - surface.draw(&vertex_buffer, no_indices, &self.program, &uniforms, &draw_params).unwrap(); + //surface.draw(&vertex_buffer, no_indices, &self.program, &uniforms, &draw_params).unwrap(); + //glium::uniforms::EmptyUniforms + + surface.draw(&vertex_buffer, no_indices, &self.program, &glium::uniforms::EmptyUniforms, &draw_params).unwrap(); }, // Draw an image whose texture data lies within the `image_map` at the diff --git a/backends/conrod_piston/examples/all_piston_window.rs b/backends/conrod_piston/examples/all_piston_window.rs index 0b6ae9968..daa52c7f0 100644 --- a/backends/conrod_piston/examples/all_piston_window.rs +++ b/backends/conrod_piston/examples/all_piston_window.rs @@ -80,6 +80,7 @@ pub fn main() { let size = window.size(); let (win_w, win_h) = (size.width as conrod_core::Scalar, size.height as conrod_core::Scalar); if let Some(e) = conrod_piston::event::convert(event.clone(), win_w, win_h) { + println!("e{:?}",e.clone()); ui.handle_event(e); } diff --git a/backends/conrod_svg/Cargo.toml b/backends/conrod_svg/Cargo.toml new file mode 100644 index 000000000..e00a6f1d9 --- /dev/null +++ b/backends/conrod_svg/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "conrod_svg" +version = "0.65.0" +authors = ["Mitchell Nordine "] +keywords = ["ui", "widgets", "gui", "interface", "graphics"] +description = "An easy-to-use, 100% Rust, extensible 2D GUI library." +license = "MIT OR Apache-2.0" +readme = "../../README.md" +repository = "https://github.com/pistondevelopers/conrod.git" +homepage = "https://github.com/pistondevelopers/conrod" +documentation = "https://docs.rs/conrod" +categories = ["gui"] + +[lib] +name = "conrod_svg" +path = "./src/lib.rs" + +[dependencies] +conrod_core = { path = "../../conrod_core", version = "0.65" } +conrod_example_shared = { path = "../conrod_example_shared", version = "0.65" } +svg = "0.5.12" +serde_json = "1.0" +cgmath = "0.16.1" diff --git a/backends/conrod_svg/src/lib.rs b/backends/conrod_svg/src/lib.rs new file mode 100644 index 000000000..e69de29bb diff --git a/ci/azure-install-node.yml b/ci/azure-install-node.yml new file mode 100644 index 000000000..9f77b4103 --- /dev/null +++ b/ci/azure-install-node.yml @@ -0,0 +1,6 @@ +steps: + - script: rustup target add wasm32-unknown-unknown + displayName: "Add WebAssembly target via rustup" + - task: NodeTool@0 + inputs: + versionSpec: '>=10.11' diff --git a/ci/azure-install-rust.yml b/ci/azure-install-rust.yml new file mode 100644 index 000000000..dd4ac1c06 --- /dev/null +++ b/ci/azure-install-rust.yml @@ -0,0 +1,25 @@ +parameters: + toolchain: 'stable' + +steps: + - bash: | + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $TOOLCHAIN + echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" + displayName: Install rust - Unix + condition: ne( variables['Agent.OS'], 'Windows_NT' ) + env: + TOOLCHAIN: ${{ parameters.toolchain }} + + - script: | + curl -sSf -o rustup-init.exe https://win.rustup.rs + rustup-init.exe -y --default-toolchain %TOOLCHAIN% + echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" + displayName: Install rust - Windows + condition: eq( variables['Agent.OS'], 'Windows_NT' ) + env: + TOOLCHAIN: ${{ parameters.toolchain }} + + - script: | + rustc -Vv + cargo -V + displayName: Query rust and cargo versions diff --git a/conrod_core/Cargo.toml b/conrod_core/Cargo.toml index 6a1400ed3..f3f92f633 100644 --- a/conrod_core/Cargo.toml +++ b/conrod_core/Cargo.toml @@ -28,4 +28,4 @@ fnv = "1.0" num = "0.2" pistoncore-input = "0.24" rusttype = { version = "0.7", features = ["gpu_cache"] } -instant = "0.1" +instant = {git="https://github.com/alanpoon/instant.git", branch="window_performance_stable", version="0.1.1", features = [ "wasm-bindgen","now" ]} diff --git a/conrod_core/src/lib.rs b/conrod_core/src/lib.rs index ab382b55a..fbbd00326 100644 --- a/conrod_core/src/lib.rs +++ b/conrod_core/src/lib.rs @@ -14,6 +14,7 @@ extern crate fnv; extern crate num; extern crate input as piston_input; extern crate rusttype; +extern crate instant; pub use color::{Color, Colorable}; pub use conrod_derive::*; diff --git a/conrod_core/src/render.rs b/conrod_core/src/render.rs index fd8ee6616..a2371b614 100644 --- a/conrod_core/src/render.rs +++ b/conrod_core/src/render.rs @@ -487,6 +487,7 @@ impl<'a> Primitives<'a> { let color = style.get_color(theme); let points = state.points.iter().cloned(); + match *style { ShapeStyle::Fill(_) => { @@ -497,6 +498,7 @@ impl<'a> Primitives<'a> { &triangles[..] }, }; + let kind = PrimitiveKind::TrianglesSingleColor { color: color.to_rgb(), triangles: &triangles, diff --git a/conrod_core/src/ui.rs b/conrod_core/src/ui.rs index 403ba4429..99e7db606 100644 --- a/conrod_core/src/ui.rs +++ b/conrod_core/src/ui.rs @@ -9,10 +9,10 @@ use std::sync::atomic::{self, AtomicUsize}; use fnv; use text; use theme::Theme; -use utils; use widget::{self, Widget}; use cursor; - +use utils; +use instant; /// A constructor type for building a `Ui` instance with a set of optional parameters. pub struct UiBuilder { /// The initial dimensions of the window in which the `Ui` exists. @@ -420,7 +420,7 @@ impl Ui { let widget = self.global_input.current.widget_capturing_mouse; let press_event = event::Ui::Press(widget, press).into(); self.global_input.push_event(press_event); - + if let MouseButton::Left = mouse_button { // Check to see if we need to uncapture the keyboard. if let Some(idx) = self.global_input.current.widget_capturing_keyboard { @@ -618,7 +618,7 @@ impl Ui { // 2. `WidgetUncapturesMouse` // 3. `WidgetCapturesMouse` Input::Motion(motion) => { - + // Create a `Motion` event. let move_ = event::Motion { motion: motion, diff --git a/conrod_core/src/widget/button.rs b/conrod_core/src/widget/button.rs index 7e68b3bbd..471bd6680 100644 --- a/conrod_core/src/widget/button.rs +++ b/conrod_core/src/widget/button.rs @@ -298,7 +298,6 @@ impl<'a> Widget for Button<'a, Flat> { fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { id, state, style, rect, ui, .. } = args; let Button { show, maybe_label, .. } = self; - let (interaction, times_triggered) = interaction_and_times_triggered(id, ui); let color = match interaction { Interaction::Idle => style.color(&ui.theme), diff --git a/workspace.toml b/workspace.toml new file mode 100644 index 000000000..4dee26684 --- /dev/null +++ b/workspace.toml @@ -0,0 +1,33 @@ +[assets] +source = 'assets/images' +destination = 'assets/resources' + +[[assets.importers]] +type = 'Texture' +extensions = [ + '.png', + '.jpg', + '.jpeg', + '.bmp', + '.tga', + '.psd', # Yes, we do supports .PSD files. +] + +[[assets.importers]] +type = 'Transmission' +extensions = [ + '.obj', + '.blend', + '.fbx', + '.gltf', + '.dae', + '.3ds', +] + +[[assets.importers]] +type = 'Audio' +extensions = [ + '.mp3', + '.wav', + '.ogg', +] \ No newline at end of file