{"action":"created","comment":{"url":"https://api.github.com/repos/citra-emu/citra-web/pulls/comments/173515308","pull_request_review_id":102724641,"id":173515308,"diff_hunk":"@@ -0,0 +1,200 @@\n++++\n+date = \"2018-02-28T17:00:00-04:00\"\n+title = \"Speeding up Citra: Improvements to the Hardware Renderer\"\n+tags = [ \"feature-update\" ]\n+author = \"jroweboy\"\n+forum = 9999\n++++\n+\n+## Cut to the chase, how fast is this?\n+\n+{{< \n+    sidebyside \"gifv\" \"/images/entry/improvements-to-hardware-renderer/\"\n+    \"before.mp4=Before\"\n+    \"after.mp4=After\" \n+>}}\n+\n+<h4 style=\"text-align: center;\">Realtime performance comparison with framelimit off</h4>\n+\n+Very fast. Test results across various computers show that it **averages out to be a 2x speed boost.**\n+With the new update, Citra will use much more of your GPU, removing some of the dependence on a CPU with high single-core performance.\n+(As always, the actual difference will vary by game and by your specific hardware configuration!)\n+In celebration of this massive improvement, we wanted to share some of the successes and struggles we've had over the years with the hardware renderer.\n+\n+## Brief History of Citra's Rendering Backends\n+\n+Back in early 2015, Citra was still a young emulator, and had just barely started displaying graphics for the first time.\n+In a momentous occasion, Citra displayed 3D graphics from a commercial game, Legend of Zelda: Ocarina of Time 3D\n+\n+{{< figure type=\"youtube\" id=\"sYukdJam6gk\" title=\"It took a year from when citra started to get to this point\" >}}\n+\n+This engineering feat was thanks to the hardwork of many contibutors in both the emulator scene and the 3ds hacking scene, who worked tirelessly to reverse engineer the 3DS GPU, a chip called the PICA200.\n+But not even a few months later, Citra was able to play the game at fullspeed! \n+\n+{{< figure type=\"youtube\" id=\"Hj8sPsB5qXQ\" title=\"Good thing that shield flashing bug still isn't around\" >}}\n+\n+Why is there such a major difference in speed from the first and the second video?\n+The speed difference boils down to how the 3DS GPU is being emulated.\n+The first video is showing off the software renderer, which emulates the PICA200 by using your computer's CPU.\n+On the other hand, the second video is using the OpenGL hardware renderer, which emulates the PICA200 by using your computer's GPU.\n+From those videos, using your GPU to emulate the 3DS GPU is the clear winner when it comes to speed!\n+However, it's not all sunshine and daisies; there's always tradeoffs in software development.\n+\n+## Challenges in the Hardware Renderer\n+\n+Earlier it was stated that the OpenGL hardware renderer was emulating the PICA200 by using the GPU instead of the CPU, and ... that's only partially true.\n+As it stands, only a portion of the PICA200 emulation is running on the GPU; most of it is running on the CPU!\n+To understand why, we need to dive a little deeper into the difference between CPUs and modern GPUs.\n+\n+{{< figure src=\"/images/entry/improvements-to-hardware-renderer/gpu_pipeline_before.png\" \n+    title=\"The green shaded portions are what Citra emulates using the GPU. For a hardware renderer, Citra isn't using the GPU much!\" \n+>}}\n+\n+As a general rule of thumb, CPUs are fast at computing general tasks, while GPUs are *blazing fast* at computing very specific tasks.\n+Whenever the tasks the PICA200 performs matches well with tasks you can do on a GPU using OpenGL, everything is fast and everyone is happy!\n+That said, we tend to run into edge cases that the PICA200 supports, but frankly, OpenGL is not well suited to support.\n+This leads to cases where sometimes we just have to [live with minor inaccuracies as a tradeoff for speed](https://github.com/citra-emu/citra/pull/2697)\n+\n+{{< figure src=\"/images/entry/improvements-to-hardware-renderer/outline_bug.png\" title=\"The infamous Pokémon outlines bug was one such example of something that OpenGL just doesn't handle well\" >}}\n+\n+OpenGL is also great for emulator developers because it's a cross-platform standard for graphics, with support for all major desktop platforms.\n+But because it's a standard, this means performance and features can vary widely between operating systems, graphics driver, and the physical graphics card.\n+As you might have guessed, this leads to some [OS specific bugs that are very hard to track down](https://github.com/citra-emu/citra/issues/2416).\n+In the linked issue, only on Mac OSX, Citra would leak memory from the hardware renderer.\n+We traced it back to how textures were juggled between the 3DS memory and the host GPU, but we don't have many developers that use Mac, so we never did find the root cause.\n+For a little bit of good news, **this is fixed in the latest nightly**, but only because the entire texture handling code was rewritten!\n+\n+## Moving Forward with the Hardware Renderer: Cleaning up Texture Forwarding\n+\n+Despite the issues mentioned above, OpenGL has been a fair choice for a hardware renderer, and [phantom](https://github.com/phanto-m/) has been hard at work making improving the renderer.\n+Their first major contribution was a massive, [complete rewrite](https://github.com/citra-emu/citra/pull/3281) of the [texture forwarding support](https://citra-emu.org/entry/texture-forwarding-brings-hd-output-to-citra/) that was added back in 2016.\n+The new texture forwarding code increases the speed of many games, and fixes upscaled rendering in some other games as well.\n+\n+{{< figure src=\"/images/entry/improvements-to-hardware-renderer/metroid_before.png\"\n+    title=\"Metroid: Samus Returns heavily used a PICA200 feature that had a comment `// TODO: implement this` in the hardware renderer\" >}}\n+{{< figure src=\"/images/entry/improvements-to-hardware-renderer/metroid_after.png\"\n+    title=\"Writing that missing method fixed upscaling while making the game much faster\" >}}\n+\n+Whenever a texture is used in the hardware renderer, the hardware renderer will try to use a copy of the texture already in the GPU memory, but if that fails, it has to reload the texture from the emulated 3DS memory.\n+This is called a texture upload, and it's slow for a good reason.\n+The communication between CPU and GPU is optimized for large amounts of data transferred, but as a tradeoff, it's not very fast.\n+This works great for PC games, where you know all the textures you want to upload ahead of time and can send them in one large batch, but ends up hurting performance for Citra since we can't know in advance when the emulated game will do something that requires a texture upload.\n+\n+The texture forwarding rewrite increases the speed of many games by adding in new checks to avoid this costly syncronization of textures between emulated 3DS memory and the host GPU memory.\n+Additionally, the new texture forwarding can avoid even more texture uploads by copying the data from an existing and compatible locations.","path":"site/content/entry/improvements-to-hardware-renderer.md","position":84,"original_position":84,"commit_id":"26842abe938dc5d945e6b2bbcbdbc5facc033044","original_commit_id":"26842abe938dc5d945e6b2bbcbdbc5facc033044","user":{"login":"Schplee","id":24275329,"avatar_url":"https://avatars2.githubusercontent.com/u/24275329?v=4","gravatar_id":"","url":"https://api.github.com/users/Schplee","html_url":"https://github.com/Schplee","followers_url":"https://api.github.com/users/Schplee/followers","following_url":"https://api.github.com/users/Schplee/following{/other_user}","gists_url":"https://api.github.com/users/Schplee/gists{/gist_id}","starred_url":"https://api.github.com/users/Schplee/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Schplee/subscriptions","organizations_url":"https://api.github.com/users/Schplee/orgs","repos_url":"https://api.github.com/users/Schplee/repos","events_url":"https://api.github.com/users/Schplee/events{/privacy}","received_events_url":"https://api.github.com/users/Schplee/received_events","type":"User","site_admin":false},"body":"`copying the data from an existing and compatible locations.`\r\n\r\n`an` should be removed","created_at":"2018-03-09T17:30:10Z","updated_at":"2018-03-09T17:43:15Z","html_url":"https://github.com/citra-emu/citra-web/pull/77#discussion_r173515308","pull_request_url":"https://api.github.com/repos/citra-emu/citra-web/pulls/77","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/citra-emu/citra-web/pulls/comments/173515308"},"html":{"href":"https://github.com/citra-emu/citra-web/pull/77#discussion_r173515308"},"pull_request":{"href":"https://api.github.com/repos/citra-emu/citra-web/pulls/77"}}},"pull_request":{"url":"https://api.github.com/repos/citra-emu/citra-web/pulls/77","id":172152377,"html_url":"https://github.com/citra-emu/citra-web/pull/77","diff_url":"https://github.com/citra-emu/citra-web/pull/77.diff","patch_url":"https://github.com/citra-emu/citra-web/pull/77.patch","issue_url":"https://api.github.com/repos/citra-emu/citra-web/issues/77","number":77,"state":"open","locked":false,"title":"Feature Report: GLVTX","user":{"login":"jroweboy","id":952515,"avatar_url":"https://avatars1.githubusercontent.com/u/952515?v=4","gravatar_id":"","url":"https://api.github.com/users/jroweboy","html_url":"https://github.com/jroweboy","followers_url":"https://api.github.com/users/jroweboy/followers","following_url":"https://api.github.com/users/jroweboy/following{/other_user}","gists_url":"https://api.github.com/users/jroweboy/gists{/gist_id}","starred_url":"https://api.github.com/users/jroweboy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jroweboy/subscriptions","organizations_url":"https://api.github.com/users/jroweboy/orgs","repos_url":"https://api.github.com/users/jroweboy/repos","events_url":"https://api.github.com/users/jroweboy/events{/privacy}","received_events_url":"https://api.github.com/users/jroweboy/received_events","type":"User","site_admin":false},"body":"This is the rough draft of the new blog post. I'm putting this up now so people can read it and find any oddities early. I'm mostly looking for feedback on style, but I'll deal with minor grammar fixes. Remember this is a rough draft so now would be the time to nitpick it to death before I consider the core content finalized\r\n\r\nPlease fact check the article as well, especially when I get the \"Challenges of PICA -> GPU\" part written.","created_at":"2018-03-01T06:16:42Z","updated_at":"2018-03-09T17:43:15Z","closed_at":null,"merged_at":null,"merge_commit_sha":"0686619c20fa121e1785c2f54a5bf920a1d332c3","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/citra-emu/citra-web/pulls/77/commits","review_comments_url":"https://api.github.com/repos/citra-emu/citra-web/pulls/77/comments","review_comment_url":"https://api.github.com/repos/citra-emu/citra-web/pulls/comments{/number}","comments_url":"https://api.github.com/repos/citra-emu/citra-web/issues/77/comments","statuses_url":"https://api.github.com/repos/citra-emu/citra-web/statuses/26842abe938dc5d945e6b2bbcbdbc5facc033044","head":{"label":"jroweboy:glvtx","ref":"glvtx","sha":"26842abe938dc5d945e6b2bbcbdbc5facc033044","user":{"login":"jroweboy","id":952515,"avatar_url":"https://avatars1.githubusercontent.com/u/952515?v=4","gravatar_id":"","url":"https://api.github.com/users/jroweboy","html_url":"https://github.com/jroweboy","followers_url":"https://api.github.com/users/jroweboy/followers","following_url":"https://api.github.com/users/jroweboy/following{/other_user}","gists_url":"https://api.github.com/users/jroweboy/gists{/gist_id}","starred_url":"https://api.github.com/users/jroweboy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jroweboy/subscriptions","organizations_url":"https://api.github.com/users/jroweboy/orgs","repos_url":"https://api.github.com/users/jroweboy/repos","events_url":"https://api.github.com/users/jroweboy/events{/privacy}","received_events_url":"https://api.github.com/users/jroweboy/received_events","type":"User","site_admin":false},"repo":{"id":122871656,"name":"citra-web","full_name":"jroweboy/citra-web","owner":{"login":"jroweboy","id":952515,"avatar_url":"https://avatars1.githubusercontent.com/u/952515?v=4","gravatar_id":"","url":"https://api.github.com/users/jroweboy","html_url":"https://github.com/jroweboy","followers_url":"https://api.github.com/users/jroweboy/followers","following_url":"https://api.github.com/users/jroweboy/following{/other_user}","gists_url":"https://api.github.com/users/jroweboy/gists{/gist_id}","starred_url":"https://api.github.com/users/jroweboy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jroweboy/subscriptions","organizations_url":"https://api.github.com/users/jroweboy/orgs","repos_url":"https://api.github.com/users/jroweboy/repos","events_url":"https://api.github.com/users/jroweboy/events{/privacy}","received_events_url":"https://api.github.com/users/jroweboy/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jroweboy/citra-web","description":"Citra website source code","fork":true,"url":"https://api.github.com/repos/jroweboy/citra-web","forks_url":"https://api.github.com/repos/jroweboy/citra-web/forks","keys_url":"https://api.github.com/repos/jroweboy/citra-web/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jroweboy/citra-web/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jroweboy/citra-web/teams","hooks_url":"https://api.github.com/repos/jroweboy/citra-web/hooks","issue_events_url":"https://api.github.com/repos/jroweboy/citra-web/issues/events{/number}","events_url":"https://api.github.com/repos/jroweboy/citra-web/events","assignees_url":"https://api.github.com/repos/jroweboy/citra-web/assignees{/user}","branches_url":"https://api.github.com/repos/jroweboy/citra-web/branches{/branch}","tags_url":"https://api.github.com/repos/jroweboy/citra-web/tags","blobs_url":"https://api.github.com/repos/jroweboy/citra-web/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jroweboy/citra-web/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jroweboy/citra-web/git/refs{/sha}","trees_url":"https://api.github.com/repos/jroweboy/citra-web/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jroweboy/citra-web/statuses/{sha}","languages_url":"https://api.github.com/repos/jroweboy/citra-web/languages","stargazers_url":"https://api.github.com/repos/jroweboy/citra-web/stargazers","contributors_url":"https://api.github.com/repos/jroweboy/citra-web/contributors","subscribers_url":"https://api.github.com/repos/jroweboy/citra-web/subscribers","subscription_url":"https://api.github.com/repos/jroweboy/citra-web/subscription","commits_url":"https://api.github.com/repos/jroweboy/citra-web/commits{/sha}","git_commits_url":"https://api.github.com/repos/jroweboy/citra-web/git/commits{/sha}","comments_url":"https://api.github.com/repos/jroweboy/citra-web/comments{/number}","issue_comment_url":"https://api.github.com/repos/jroweboy/citra-web/issues/comments{/number}","contents_url":"https://api.github.com/repos/jroweboy/citra-web/contents/{+path}","compare_url":"https://api.github.com/repos/jroweboy/citra-web/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jroweboy/citra-web/merges","archive_url":"https://api.github.com/repos/jroweboy/citra-web/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jroweboy/citra-web/downloads","issues_url":"https://api.github.com/repos/jroweboy/citra-web/issues{/number}","pulls_url":"https://api.github.com/repos/jroweboy/citra-web/pulls{/number}","milestones_url":"https://api.github.com/repos/jroweboy/citra-web/milestones{/number}","notifications_url":"https://api.github.com/repos/jroweboy/citra-web/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jroweboy/citra-web/labels{/name}","releases_url":"https://api.github.com/repos/jroweboy/citra-web/releases{/id}","deployments_url":"https://api.github.com/repos/jroweboy/citra-web/deployments","created_at":"2018-02-25T20:05:11Z","updated_at":"2018-02-25T20:05:14Z","pushed_at":"2018-03-04T04:33:37Z","git_url":"git://github.com/jroweboy/citra-web.git","ssh_url":"git@github.com:jroweboy/citra-web.git","clone_url":"https://github.com/jroweboy/citra-web.git","svn_url":"https://github.com/jroweboy/citra-web","homepage":"","size":75431,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"citra-emu:master","ref":"master","sha":"98409254c26ed06e39a67f85ef155481c61166c0","user":{"login":"citra-emu","id":7605670,"avatar_url":"https://avatars1.githubusercontent.com/u/7605670?v=4","gravatar_id":"","url":"https://api.github.com/users/citra-emu","html_url":"https://github.com/citra-emu","followers_url":"https://api.github.com/users/citra-emu/followers","following_url":"https://api.github.com/users/citra-emu/following{/other_user}","gists_url":"https://api.github.com/users/citra-emu/gists{/gist_id}","starred_url":"https://api.github.com/users/citra-emu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/citra-emu/subscriptions","organizations_url":"https://api.github.com/users/citra-emu/orgs","repos_url":"https://api.github.com/users/citra-emu/repos","events_url":"https://api.github.com/users/citra-emu/events{/privacy}","received_events_url":"https://api.github.com/users/citra-emu/received_events","type":"Organization","site_admin":false},"repo":{"id":83190707,"name":"citra-web","full_name":"citra-emu/citra-web","owner":{"login":"citra-emu","id":7605670,"avatar_url":"https://avatars1.githubusercontent.com/u/7605670?v=4","gravatar_id":"","url":"https://api.github.com/users/citra-emu","html_url":"https://github.com/citra-emu","followers_url":"https://api.github.com/users/citra-emu/followers","following_url":"https://api.github.com/users/citra-emu/following{/other_user}","gists_url":"https://api.github.com/users/citra-emu/gists{/gist_id}","starred_url":"https://api.github.com/users/citra-emu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/citra-emu/subscriptions","organizations_url":"https://api.github.com/users/citra-emu/orgs","repos_url":"https://api.github.com/users/citra-emu/repos","events_url":"https://api.github.com/users/citra-emu/events{/privacy}","received_events_url":"https://api.github.com/users/citra-emu/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/citra-emu/citra-web","description":"Citra website source code","fork":false,"url":"https://api.github.com/repos/citra-emu/citra-web","forks_url":"https://api.github.com/repos/citra-emu/citra-web/forks","keys_url":"https://api.github.com/repos/citra-emu/citra-web/keys{/key_id}","collaborators_url":"https://api.github.com/repos/citra-emu/citra-web/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/citra-emu/citra-web/teams","hooks_url":"https://api.github.com/repos/citra-emu/citra-web/hooks","issue_events_url":"https://api.github.com/repos/citra-emu/citra-web/issues/events{/number}","events_url":"https://api.github.com/repos/citra-emu/citra-web/events","assignees_url":"https://api.github.com/repos/citra-emu/citra-web/assignees{/user}","branches_url":"https://api.github.com/repos/citra-emu/citra-web/branches{/branch}","tags_url":"https://api.github.com/repos/citra-emu/citra-web/tags","blobs_url":"https://api.github.com/repos/citra-emu/citra-web/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/citra-emu/citra-web/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/citra-emu/citra-web/git/refs{/sha}","trees_url":"https://api.github.com/repos/citra-emu/citra-web/git/trees{/sha}","statuses_url":"https://api.github.com/repos/citra-emu/citra-web/statuses/{sha}","languages_url":"https://api.github.com/repos/citra-emu/citra-web/languages","stargazers_url":"https://api.github.com/repos/citra-emu/citra-web/stargazers","contributors_url":"https://api.github.com/repos/citra-emu/citra-web/contributors","subscribers_url":"https://api.github.com/repos/citra-emu/citra-web/subscribers","subscription_url":"https://api.github.com/repos/citra-emu/citra-web/subscription","commits_url":"https://api.github.com/repos/citra-emu/citra-web/commits{/sha}","git_commits_url":"https://api.github.com/repos/citra-emu/citra-web/git/commits{/sha}","comments_url":"https://api.github.com/repos/citra-emu/citra-web/comments{/number}","issue_comment_url":"https://api.github.com/repos/citra-emu/citra-web/issues/comments{/number}","contents_url":"https://api.github.com/repos/citra-emu/citra-web/contents/{+path}","compare_url":"https://api.github.com/repos/citra-emu/citra-web/compare/{base}...{head}","merges_url":"https://api.github.com/repos/citra-emu/citra-web/merges","archive_url":"https://api.github.com/repos/citra-emu/citra-web/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/citra-emu/citra-web/downloads","issues_url":"https://api.github.com/repos/citra-emu/citra-web/issues{/number}","pulls_url":"https://api.github.com/repos/citra-emu/citra-web/pulls{/number}","milestones_url":"https://api.github.com/repos/citra-emu/citra-web/milestones{/number}","notifications_url":"https://api.github.com/repos/citra-emu/citra-web/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/citra-emu/citra-web/labels{/name}","releases_url":"https://api.github.com/repos/citra-emu/citra-web/releases{/id}","deployments_url":"https://api.github.com/repos/citra-emu/citra-web/deployments","created_at":"2017-02-26T07:19:50Z","updated_at":"2018-03-06T21:52:16Z","pushed_at":"2018-03-04T04:33:38Z","git_url":"git://github.com/citra-emu/citra-web.git","ssh_url":"git@github.com:citra-emu/citra-web.git","clone_url":"https://github.com/citra-emu/citra-web.git","svn_url":"https://github.com/citra-emu/citra-web","homepage":"","size":68959,"stargazers_count":9,"watchers_count":9,"language":"HTML","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":12,"mirror_url":null,"archived":false,"open_issues_count":7,"license":{"key":"agpl-3.0","name":"GNU Affero General Public License v3.0","spdx_id":"AGPL-3.0","url":"https://api.github.com/licenses/agpl-3.0"},"forks":12,"open_issues":7,"watchers":9,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/citra-emu/citra-web/pulls/77"},"html":{"href":"https://github.com/citra-emu/citra-web/pull/77"},"issue":{"href":"https://api.github.com/repos/citra-emu/citra-web/issues/77"},"comments":{"href":"https://api.github.com/repos/citra-emu/citra-web/issues/77/comments"},"review_comments":{"href":"https://api.github.com/repos/citra-emu/citra-web/pulls/77/comments"},"review_comment":{"href":"https://api.github.com/repos/citra-emu/citra-web/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/citra-emu/citra-web/pulls/77/commits"},"statuses":{"href":"https://api.github.com/repos/citra-emu/citra-web/statuses/26842abe938dc5d945e6b2bbcbdbc5facc033044"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"Schplee"}}