Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling lost gpu context on stage3d #289

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/flambe/System.hx
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,21 @@ class System
*/
public static var volume (default, null) :AnimatedFloat = new AnimatedFloat(1);

/**
* Used to indicate whether asynchronous platform initialization has finished
*/
public static var promise (default, null) :Promise<Bool>;

/**
* Starts up Flambe, this should usually be the first thing a game does.
*/
public static function init ()
{
if (!_calledInit) {
_platform.init();
promise = _platform.init();
_calledInit = true;
}
return promise;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/flambe/platform/InternalRenderer.hx
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ interface InternalRenderer<NativeImage> extends RendererSystem<NativeImage>
* Notifies the renderer that drawing the frame is complete.
*/
function didRender () :Void;

/**
* Returns true if rendering can be done, false otherwise (e.g. gpu context is lost).
*/
function canRender () :Bool;
}
2 changes: 1 addition & 1 deletion src/flambe/platform/Platform.hx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import flambe.util.Promise;

interface Platform
{
function init () :Void;
function init () :Promise<Bool>;

function getExternal () :ExternalSystem;
function getKeyboard () :KeyboardSystem;
Expand Down
16 changes: 13 additions & 3 deletions src/flambe/platform/flash/FlashPlatform.hx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class FlashPlatform
public function init ()
{
var stage = Lib.current.stage;
var promise = new Promise<Bool>();
promise.success.connect(function (result) {
Log.info("Initialized Flash platform", ["renderer", _renderer.type]);
});

_stage = new FlashStage(stage);
_pointer = new BasicPointer();
Expand All @@ -47,7 +51,12 @@ class FlashPlatform
_touch = new DummyTouch();
#end

_renderer = new Stage3DRenderer();
var stage3DRenderer = new Stage3DRenderer();
_renderer = stage3DRenderer;
stage3DRenderer.promise.success.connect(function (result) {
// Stage3DRenderer's initialization is the only asynchronous part of FlashPlatform's init
promise.result = result;
});
mainLoop = new MainLoop();

stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
Expand Down Expand Up @@ -96,7 +105,7 @@ class FlashPlatform
new DebugLogic(this);
_catapult = FlashCatapultClient.canUse() ? new FlashCatapultClient() : null;
#end
Log.info("Initialized Flash platform", ["renderer", _renderer.type]);
return promise;
}

public function loadAssetPack (manifest :Manifest) :Promise<AssetPack>
Expand Down Expand Up @@ -238,7 +247,8 @@ class FlashPlatform

private function onRender (_)
{
mainLoop.render(_renderer);
if(_renderer.canRender())
mainLoop.render(_renderer);
}

private function onUncaughtError (event :UncaughtErrorEvent)
Expand Down
3 changes: 3 additions & 0 deletions src/flambe/platform/flash/FlashStage.hx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ class FlashStage

private function onResize (_)
{
#if flash
if (!FlashPlatform.instance.getRenderer().canRender()) return;
#end
resize.emit();
}

Expand Down
47 changes: 45 additions & 2 deletions src/flambe/platform/flash/Stage3DRenderer.hx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import flash.display.Stage3D;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.Lib;
import hxsl.Shader;

import haxe.io.Bytes;

Expand All @@ -18,6 +19,7 @@ import flambe.display.Graphics;
import flambe.display.Texture;
import flambe.subsystem.RendererSystem;
import flambe.util.Assert;
import flambe.util.Promise;
import flambe.util.Value;

class Stage3DRenderer
Expand All @@ -31,9 +33,23 @@ class Stage3DRenderer

public var batcher (default, null) :Stage3DBatcher;

/**
* Used to indicate whether a Context3D has been created yet, as this is the only asynchronous point of Flash init
*/
public var promise : Promise<Bool>;

#if stage3d_handle_context_loss
private var rootToData:Map<Stage3DTextureRoot, BitmapData>;
#end

public function new ()
{
_hasGPU = new Value<Bool>(false);
promise = new Promise<Bool>();
#if stage3d_handle_context_loss
rootToData = new Map();
_hasGPU.changed.connect(handleContextLoss, true);
#end

// Use the first available Stage3D
var stage = Lib.current.stage;
Expand All @@ -55,6 +71,23 @@ class Stage3DRenderer
}
Log.error("No free Stage3Ds available!");
}

public function canRender():Bool {
return _context3D != null && _context3D.driverInfo != "Disposed";
}

#if stage3d_handle_context_loss
function handleContextLoss(has:Bool, didHave:Bool) :Void
{
if (has && promise.hasResult) {//context was created and it's not the first context
Log.info("Stage3D GPU context was lost, reuploading textures");
for (root in rootToData.keys()) {
root.init(_context3D, false);
root.uploadBitmapData(rootToData.get(root));
}
}
}
#end

inline private function get_type () :RendererType
{
Expand All @@ -76,9 +109,12 @@ class Stage3DRenderer
if (_context3D == null) {
return null; // No Stage3D context yet
}

var bitmapData :BitmapData = cast bitmapData;
var root = new Stage3DTextureRoot(this, bitmapData.width, bitmapData.height);
#if stage3d_handle_context_loss
rootToData.set(root, bitmapData.clone());
#end
root.init(_context3D, false);
root.uploadBitmapData(bitmapData);
return root.createTexture(bitmapData.width, bitmapData.height);
Expand Down Expand Up @@ -142,8 +178,15 @@ class Stage3DRenderer
onResize(null);

// Signal that the GPU context was (re)created
ShaderGlobals.disposeAll(true);
hasGPU._ = false;
hasGPU._ = true;

// initialization of the Renderer has completed if a Context3D has been created
if (!promise.hasResult)
{
promise.result = true;
}
}

private function onError (event :ErrorEvent)
Expand All @@ -153,7 +196,7 @@ class Stage3DRenderer

private function onResize (_)
{
if (graphics != null) {
if (graphics != null && canRender()) {
var stage = Lib.current.stage;
batcher.resizeBackbuffer(stage.stageWidth, stage.stageHeight);
graphics.onResize(stage.stageWidth, stage.stageHeight);
Expand Down
4 changes: 4 additions & 0 deletions src/flambe/platform/html/CanvasRenderer.hx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class CanvasRenderer
{
graphics.didRender();
}

public function canRender() {
return true;
}

public function getName () :String
{
Expand Down
7 changes: 6 additions & 1 deletion src/flambe/platform/html/HtmlPlatform.hx
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,12 @@ class HtmlPlatform
_catapult = HtmlCatapultClient.canUse() ? new HtmlCatapultClient() : null;
#end
#end
Log.info("Initialized HTML platform", ["renderer", _renderer.type]);
var promise = new Promise<Bool>();
promise.success.connect(function (result) {
Log.info("Initialized HTML platform", ["renderer", _renderer.type]);
});
promise.result = true;
return promise;
}

public function loadAssetPack (manifest :Manifest) :Promise<AssetPack>
Expand Down
4 changes: 4 additions & 0 deletions src/flambe/platform/html/WebGLRenderer.hx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ class WebGLRenderer
{
graphics.didRender();
}

public function canRender() {
return !gl.isContextLost();
}

private function onResize ()
{
Expand Down