Various fixes and improvements
This commit is contained in:
parent
803415c988
commit
f9a8376b93
@ -45,6 +45,10 @@
|
||||
*.Input.Touch.TouchLocationState
|
||||
*.Input.Touch.TouchPanel
|
||||
*.Input.Touch.TouchPanelCapabilities
|
||||
*.Input.Keyboard
|
||||
*.Input.KeyboardState
|
||||
*.Input.Keys
|
||||
*.Input.KeyState
|
||||
|
||||
*.Graphics.BasicEffect
|
||||
*.Graphics.Blend
|
||||
|
@ -11,11 +11,45 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
protected override void onCreate(android.os.Bundle savedInstanceState)
|
||||
{
|
||||
// on some devices, this should be before call to base.onCreate
|
||||
// requestWindowFeature(android.view.Window.FEATURE_NO_TITLE);
|
||||
|
||||
logTag = GetMetaAttr_Str("log.tag", "BNA_Game");
|
||||
|
||||
backKeyCode = GetMetaAttr_Int("back.key");
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= 19)
|
||||
{
|
||||
immersiveMode = GetMetaAttr_Int("immersive.mode") != 0;
|
||||
|
||||
if (immersiveMode && android.os.Build.VERSION.SDK_INT >= 28)
|
||||
{
|
||||
var layoutParams = getWindow().getAttributes();
|
||||
layoutParams.layoutInDisplayCutoutMode =
|
||||
android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
||||
getWindow().setAttributes(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMetaAttr_Int("keep.screen.on") != 0)
|
||||
{
|
||||
getWindow().addFlags(
|
||||
android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
/*
|
||||
int flags = android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
| android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
|
||||
| android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
|
||||
if (GetMetaAttr_Int("keep.screen.on") != 0)
|
||||
flags |= android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
||||
getWindow().setFlags(flags
|
||||
| android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||
flags);
|
||||
*/
|
||||
|
||||
base.onCreate(savedInstanceState);
|
||||
|
||||
_LogTag = GetMetaAttr("log.tag") ?? _LogTag;
|
||||
|
||||
new java.lang.Thread(gameRunner = new GameRunner(this)).start();
|
||||
}
|
||||
|
||||
//
|
||||
@ -36,7 +70,7 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
//
|
||||
// Android events forwarded to GameRunner:
|
||||
// onPause, onResume, onDestroy, onTouchEvent
|
||||
// onPause, onWindowFocusChanged, onDestroy, onTouchEvent, onBackPressed
|
||||
//
|
||||
|
||||
protected override void onPause()
|
||||
@ -45,11 +79,39 @@ namespace Microsoft.Xna.Framework
|
||||
base.onPause();
|
||||
}
|
||||
|
||||
protected override void onResume()
|
||||
public override void onWindowFocusChanged(bool hasFocus)
|
||||
{
|
||||
base.onWindowFocusChanged(hasFocus);
|
||||
|
||||
if (hasFocus)
|
||||
{
|
||||
if (immersiveMode)
|
||||
{
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| android.view.View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
}
|
||||
|
||||
if (object.ReferenceEquals(gameRunner, null))
|
||||
{
|
||||
new java.lang.Thread(gameRunner = new GameRunner(this)).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
gameRunner.ActivityResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*protected override void onResume()
|
||||
{
|
||||
gameRunner?.ActivityResume();
|
||||
base.onResume();
|
||||
}
|
||||
}*/
|
||||
|
||||
protected override void onDestroy()
|
||||
{
|
||||
@ -79,33 +141,47 @@ namespace Microsoft.Xna.Framework
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void onBackPressed()
|
||||
{
|
||||
if (backKeyCode != 0)
|
||||
gameRunner?.ActivityKey(backKeyCode);
|
||||
else
|
||||
base.onBackPressed();
|
||||
}
|
||||
|
||||
//
|
||||
// GetMetaAttr
|
||||
// GetMetaAttr_Str, GetMetaAttr_Int
|
||||
//
|
||||
|
||||
public string GetMetaAttr(string name, bool warn = false)
|
||||
public string GetMetaAttr_Str(string name, string def)
|
||||
{
|
||||
var info = getPackageManager().getActivityInfo(getComponentName(),
|
||||
android.content.pm.PackageManager.GET_ACTIVITIES
|
||||
| android.content.pm.PackageManager.GET_META_DATA);
|
||||
name = "microsoft.xna.framework." + name;
|
||||
var str = info?.metaData?.getString(name);
|
||||
name = "BNA." + name;
|
||||
var str = GetMetaData()?.getString(name);
|
||||
if (string.IsNullOrEmpty(str))
|
||||
{
|
||||
if (warn)
|
||||
Activity.Log($"missing metadata attribute '{name}'");
|
||||
str = null;
|
||||
str = def;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public int GetMetaAttr_Int(string name)
|
||||
=> GetMetaData()?.getInt("BNA." + name) ?? 0;
|
||||
|
||||
private android.os.Bundle GetMetaData()
|
||||
=> getPackageManager().getActivityInfo(
|
||||
getComponentName(),
|
||||
android.content.pm.PackageManager.GET_ACTIVITIES
|
||||
| android.content.pm.PackageManager.GET_META_DATA)
|
||||
?.metaData;
|
||||
|
||||
//
|
||||
// Log
|
||||
//
|
||||
|
||||
public static void Log(string s) => android.util.Log.i(_LogTag, s);
|
||||
public static void Log(string s) => android.util.Log.i(logTag, s);
|
||||
|
||||
private static string _LogTag = "BNA_Game";
|
||||
private static string logTag;
|
||||
|
||||
//
|
||||
// data
|
||||
@ -113,6 +189,8 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
private GameRunner gameRunner;
|
||||
private bool restartActivity;
|
||||
private bool immersiveMode;
|
||||
private int backKeyCode;
|
||||
|
||||
}
|
||||
|
||||
|
13
BNA/src/Debug.cs
Normal file
13
BNA/src/Debug.cs
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
namespace System.Diagnostics
|
||||
{
|
||||
|
||||
public static class Debug
|
||||
{
|
||||
public static void WriteLine(string message)
|
||||
{
|
||||
Microsoft.Xna.Framework.GameRunner.Log(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -67,7 +67,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public string CreateProgram2(string vertexText, string fragmentText)
|
||||
{
|
||||
string errText = null;
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send( () =>
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send(true, () =>
|
||||
{
|
||||
(vertexId, errText) = CompileShader(
|
||||
GLES20.GL_VERTEX_SHADER, "vertex", vertexText);
|
||||
@ -93,7 +93,6 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
|
||||
(int, string) CompileShader(int kind, string errKind, string text)
|
||||
{
|
||||
//GameRunner.Log($"SHADER PROGRAM: [[[" + text + "]]]");
|
||||
string errText = null;
|
||||
int shaderId = GLES20.glCreateShader(kind);
|
||||
int errCode = GLES20.glGetError();
|
||||
@ -237,7 +236,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public (string name, int type, int size)[] GetProgramUniforms()
|
||||
{
|
||||
(string, int, int)[] result = null;
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send( () =>
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send(true, () =>
|
||||
{
|
||||
var count = new int[1];
|
||||
GLES20.glGetProgramiv(programId, GLES20.GL_ACTIVE_UNIFORM_MAX_LENGTH, count, 0);
|
||||
@ -272,7 +271,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public void INTERNAL_applyEffect(uint pass)
|
||||
{
|
||||
var graphicsDevice = GraphicsDevice;
|
||||
Renderer.Get(graphicsDevice.GLDevice).Send( () =>
|
||||
Renderer.Get(graphicsDevice.GLDevice).Send(false, () =>
|
||||
{
|
||||
GLES20.glUseProgram(programId);
|
||||
int n = Parameters.Count;
|
||||
@ -297,7 +296,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
if (! base.IsDisposed)
|
||||
{
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send( () =>
|
||||
Renderer.Get(GraphicsDevice.GLDevice).Send(true, () =>
|
||||
{
|
||||
GLES20.glDeleteShader(fragmentId);
|
||||
GLES20.glDeleteShader(vertexId);
|
||||
|
@ -40,7 +40,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
GLES20.glViewport(v.x, v.y, v.w, v.h);
|
||||
GLES20.glDepthRangef(v.minDepth, v.maxDepth);
|
||||
@ -54,7 +54,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public static void FNA3D_SetScissorRect(IntPtr device, ref Rectangle scissor)
|
||||
{
|
||||
var s = scissor;
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
GLES20.glScissor(s.X, s.Y, s.Width, s.Height);
|
||||
});
|
||||
@ -69,7 +69,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
var clearColor = color;
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
var WriteMask = state.WriteMask;
|
||||
@ -180,8 +180,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Present();
|
||||
Renderer.Get(device).Present();
|
||||
}
|
||||
|
||||
//
|
||||
@ -192,7 +191,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
var input = blendState;
|
||||
var renderer = Renderer.Get(device);
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
|
||||
@ -323,7 +322,6 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public static void FNA3D_SetDepthStencilState(IntPtr device,
|
||||
ref FNA3D_DepthStencilState depthStencilState)
|
||||
{
|
||||
// AndroidActivity.LogI(">>>>> SET DEPTH AND STENCIL STATE");
|
||||
}
|
||||
|
||||
//
|
||||
@ -335,7 +333,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
var input = rasterizerState;
|
||||
var renderer = Renderer.Get(device);
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
|
||||
@ -382,6 +380,30 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_GetBackbufferSize
|
||||
//
|
||||
|
||||
public static void FNA3D_GetBackbufferSize(IntPtr device, out int w, out int h)
|
||||
{
|
||||
var bounds = GameRunner.Singleton.ClientBounds;
|
||||
w = bounds.Width;
|
||||
h = bounds.Height;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_GetBackbufferSurfaceFormat
|
||||
//
|
||||
|
||||
public static SurfaceFormat FNA3D_GetBackbufferSurfaceFormat(IntPtr device)
|
||||
{
|
||||
return SurfaceFormat.Color;
|
||||
}
|
||||
|
||||
//
|
||||
// FNA3D_GetMaxMultiSampleCount
|
||||
//
|
||||
|
||||
public static int FNA3D_GetMaxMultiSampleCount(IntPtr device, SurfaceFormat format,
|
||||
int preferredMultiSampleCount)
|
||||
{
|
||||
@ -426,7 +448,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int indexOffset = startIndex * elementSize;
|
||||
primitiveCount = PrimitiveCount(primitiveType, primitiveCount);
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, (int) indices);
|
||||
GLES30.glDrawRangeElements(drawMode, minVertexIndex, maxVertexIndex,
|
||||
@ -464,7 +486,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int drawMode = PrimitiveTypeToDrawMode[(int) primitiveType];
|
||||
primitiveCount = PrimitiveCount(primitiveType, primitiveCount);
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
GLES20.glDrawArrays(drawMode, vertexStart, primitiveCount);
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
private static int CreateBuffer(Renderer renderer, int target, byte dynamic, int size)
|
||||
{
|
||||
int bufferId = 0;
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
int[] id = new int[1];
|
||||
GLES20.glGenBuffers(1, id, 0);
|
||||
@ -74,7 +74,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public static void FNA3D_AddDisposeVertexBuffer(IntPtr device, IntPtr buffer)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
GLES20.glDeleteBuffers(1, new int[] { (int) buffer }, 0);
|
||||
|
||||
@ -102,7 +102,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
var dataBuffer = BufferSerializer.Convert(
|
||||
dataPointer, dataLength, state, bufferId);
|
||||
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
GLES20.glBindBuffer(target, bufferId);
|
||||
|
||||
@ -151,11 +151,18 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int numBindings, byte bindingsUpdated,
|
||||
int baseVertex)
|
||||
{
|
||||
/* skip if FNA says the bindings have not been updated
|
||||
if (baseVertex == ((State) renderer.UserData).BaseVertex.getAndSet(baseVertex))
|
||||
{
|
||||
if (bindingsUpdated == 0)
|
||||
return;
|
||||
}*/
|
||||
|
||||
var bindingsCopy = new FNA3D_VertexBufferBinding[numBindings];
|
||||
for (int i = 0; i < numBindings; i++)
|
||||
bindingsCopy[i] = bindings[i];
|
||||
|
||||
Renderer.Get(device).Send( () =>
|
||||
Renderer.Get(device).Send(false, () =>
|
||||
{
|
||||
int nextAttribIndex = 0;
|
||||
foreach (var binding in bindingsCopy)
|
||||
@ -278,7 +285,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
// we use GCHandle::FromIntPtr to convert that address to an object reference.
|
||||
// see also: system.runtime.interopservices.GCHandle struct in baselib.
|
||||
int offset = (int) data;
|
||||
newBuffer = Convert(GCHandle.FromIntPtr(data - offset).Target,
|
||||
newBuffer = Convert2(GCHandle.FromIntPtr(data - offset).Target,
|
||||
offset, length, oldBuffer);
|
||||
|
||||
if (newBuffer != oldBuffer)
|
||||
@ -293,7 +300,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
}
|
||||
|
||||
|
||||
public static java.nio.Buffer Convert(object data, int offset, int length,
|
||||
private static java.nio.Buffer Convert2(object data, int offset, int length,
|
||||
java.nio.Buffer buffer)
|
||||
{
|
||||
if (data is short[])
|
||||
@ -491,6 +498,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
public Dictionary<int, int[]> BufferSizeUsage = new Dictionary<int, int[]>();
|
||||
public Dictionary<int, java.nio.Buffer> BufferCache = new Dictionary<int, java.nio.Buffer>();
|
||||
//public java.util.concurrent.atomic.AtomicInteger BaseVertex = new java.util.concurrent.atomic.AtomicInteger();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,20 +27,23 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
default: throw new ArgumentException("depthStencilFormat");
|
||||
}
|
||||
|
||||
int swapInterval = presentationParameters.presentationInterval switch
|
||||
{
|
||||
PresentInterval.Two => 2,
|
||||
PresentInterval.Immediate => 0,
|
||||
// Default, One, or any other value:
|
||||
_ => 1
|
||||
};
|
||||
|
||||
bool checkErrors = GameRunner.Singleton.CheckGlErrors();
|
||||
|
||||
var device = Renderer.Create(GameRunner.Singleton.Activity,
|
||||
GameRunner.Singleton.OnSurfaceChanged,
|
||||
8, 8, 8, 0, depthSize, stencilSize);
|
||||
8, 8, 8, 0, depthSize, stencilSize,
|
||||
swapInterval, checkErrors);
|
||||
|
||||
FNA3D_ResetBackbuffer(device, ref presentationParameters);
|
||||
return device;
|
||||
|
||||
/*var renderer = Renderer.Get(device);
|
||||
renderer.UserData = new State()
|
||||
{
|
||||
BackBufferWidth = presentationParameters.backBufferWidth,
|
||||
BackBufferHeight = presentationParameters.backBufferHeight,
|
||||
AdjustViewport = // see also FNA3D_SetViewport
|
||||
(presentationParameters.displayOrientation == DisplayOrientation.Default)
|
||||
};*/
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -27,7 +27,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
renderTargetsCopy[i] = renderTargets[i];
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
if (state.TargetFramebuffer == 0)
|
||||
@ -99,7 +99,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
throw new PlatformNotSupportedException();
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
GLES20.glBindFramebuffer(GLES30.GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
@ -132,7 +132,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int texture = (int) renderTarget.texture;
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
int textureUnit = GLES20.GL_TEXTURE0 + renderer.TextureUnits - 1;
|
||||
GLES20.glActiveTexture(textureUnit);
|
||||
@ -165,7 +165,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int bufferId = 0;
|
||||
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
|
||||
@ -192,7 +192,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public static void FNA3D_AddDisposeRenderbuffer(IntPtr device, IntPtr renderbuffer)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
GLES20.glDeleteRenderbuffers(1, new int[] { (int) renderbuffer }, 0);
|
||||
});
|
||||
@ -216,6 +216,8 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int x, int y, int w, int h, int level,
|
||||
object dataObject, int dataOffset, int dataLength)
|
||||
{
|
||||
int[] tempIntArray = null;
|
||||
|
||||
java.nio.Buffer buffer = dataObject switch
|
||||
{
|
||||
sbyte[] byteArray =>
|
||||
@ -224,10 +226,18 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int[] intArray =>
|
||||
java.nio.IntBuffer.wrap(intArray, dataOffset / 4, dataLength / 4),
|
||||
|
||||
Color[] _ =>
|
||||
// GameRunner constructor sets the marshal size of Color to -1,
|
||||
// so we expect only negative or zero values here
|
||||
java.nio.IntBuffer.wrap(
|
||||
tempIntArray = new int[dataLength <= 0
|
||||
? (dataLength = -dataLength)
|
||||
: throw new ArgumentException()]),
|
||||
|
||||
_ => throw new ArgumentException(dataObject?.GetType().ToString()),
|
||||
};
|
||||
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
if (state.SourceFramebuffer == 0)
|
||||
@ -257,6 +267,16 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
|
||||
GLES20.glBindFramebuffer(GLES30.GL_READ_FRAMEBUFFER, 0);
|
||||
});
|
||||
|
||||
if (tempIntArray != null)
|
||||
{
|
||||
if (dataOffset > 0)
|
||||
throw new ArgumentException();
|
||||
// convert int[] array from the GL call to a Color[] array
|
||||
var colorArray = (Color[]) dataObject;
|
||||
for (int i = 0; i < dataLength; i++)
|
||||
colorArray[i - dataOffset].PackedValue = (uint) tempIntArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -90,7 +90,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
}
|
||||
|
||||
int textureId = 0;
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
int id = CreateTexture(renderer, GLES20.GL_TEXTURE_2D, format, levelCount);
|
||||
if (id != 0)
|
||||
@ -138,7 +138,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
public static void FNA3D_AddDisposeTexture(IntPtr device, IntPtr texture)
|
||||
{
|
||||
var renderer = Renderer.Get(device);
|
||||
renderer.Send( () =>
|
||||
renderer.Send(true, () =>
|
||||
{
|
||||
GLES20.glDeleteTextures(1, new int[] { (int) texture }, 0);
|
||||
|
||||
@ -165,10 +165,13 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int[] intArray =>
|
||||
java.nio.IntBuffer.wrap(intArray, dataOffset / 4, dataLength / 4),
|
||||
|
||||
Color[] colorArray =>
|
||||
bufferFromColorArray(colorArray, dataOffset, dataLength),
|
||||
|
||||
_ => throw new ArgumentException(dataObject?.GetType().ToString()),
|
||||
};
|
||||
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
|
||||
|
||||
@ -200,6 +203,20 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
java.nio.Buffer bufferFromColorArray(Color[] array, int offset, int length)
|
||||
{
|
||||
// GameRunner constructor sets the marshal size of Color to -1,
|
||||
// so we expect only negative or zero values here
|
||||
if (offset > 0 || length > 0)
|
||||
throw new ArgumentException();
|
||||
// convert Color[] array into an int[] array for the GL call
|
||||
length = -length;
|
||||
var intArray = new int[length];
|
||||
for (int i = 0; i < length; i++)
|
||||
intArray[i] = (int) array[i - offset].PackedValue;
|
||||
return java.nio.IntBuffer.wrap(intArray);
|
||||
}
|
||||
}
|
||||
|
||||
public static void FNA3D_SetTextureData2D(IntPtr device, IntPtr texture,
|
||||
@ -344,7 +361,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
int textureId = (int) texture;
|
||||
var renderer = Renderer.Get(device);
|
||||
|
||||
renderer.Send( () =>
|
||||
renderer.Send(false, () =>
|
||||
{
|
||||
var state = (State) renderer.UserData;
|
||||
var config = state.TextureConfigs[textureId];
|
||||
|
@ -14,8 +14,6 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
private Activity activity;
|
||||
private System.Collections.Hashtable dict;
|
||||
private int clientWidth, clientHeight;
|
||||
private Rectangle clientBounds;
|
||||
private bool recreateActivity;
|
||||
|
||||
private java.util.concurrent.atomic.AtomicInteger inModal;
|
||||
@ -31,6 +29,9 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
private const int CONFIG_EVENT = 1;
|
||||
private const int TOUCH_EVENT = 2;
|
||||
private const int KEY_EVENT = 4;
|
||||
|
||||
private int eventKeyCode;
|
||||
|
||||
//
|
||||
// constructor
|
||||
@ -40,7 +41,15 @@ namespace Microsoft.Xna.Framework
|
||||
{
|
||||
this.activity = activity;
|
||||
|
||||
UpdateConfiguration(false);
|
||||
// in Bluebonnet, the following method is used to specify the
|
||||
// size that Marshal.SizeOf should return for non-primitive types.
|
||||
// this is used to enable Texture2D.GetData/SetData to accept
|
||||
// Color[] arrays. see also SetTextureData in FNA3D_Tex
|
||||
System.Runtime.InteropServices.Marshal.SetComObjectData(
|
||||
typeof(System.Runtime.InteropServices.Marshal),
|
||||
typeof(Color), -1);
|
||||
|
||||
UpdateConfiguration();
|
||||
|
||||
inModal = new java.util.concurrent.atomic.AtomicInteger();
|
||||
shouldPause = new java.util.concurrent.atomic.AtomicInteger();
|
||||
@ -71,6 +80,12 @@ namespace Microsoft.Xna.Framework
|
||||
set => inModal.set(value ? 1 : 0);
|
||||
}
|
||||
|
||||
//
|
||||
// CheckGlErrors
|
||||
//
|
||||
|
||||
public bool CheckGlErrors() => activity.GetMetaAttr_Int("check.gl.errors") != 0;
|
||||
|
||||
//
|
||||
// Thread run() method
|
||||
//
|
||||
@ -103,6 +118,8 @@ namespace Microsoft.Xna.Framework
|
||||
GameRunner.Log("========================================");
|
||||
GameRunner.Log(e.ToString());
|
||||
GameRunner.Log("========================================");
|
||||
if (! object.ReferenceEquals(e.InnerException, null))
|
||||
e = e.InnerException;
|
||||
System.Windows.Forms.MessageBox.Show(e.ToString());
|
||||
}
|
||||
|
||||
@ -111,19 +128,13 @@ namespace Microsoft.Xna.Framework
|
||||
{
|
||||
Type clsType = null;
|
||||
|
||||
var clsName = activity.GetMetaAttr("main.class", true);
|
||||
if (clsName != null)
|
||||
{
|
||||
var clsName = activity.GetMetaAttr_Str("main.class", ".Program");
|
||||
if (clsName[0] == '.')
|
||||
clsName = activity.getPackageName() + clsName;
|
||||
|
||||
clsType = System.Type.GetType(clsName, false, true);
|
||||
}
|
||||
|
||||
if (clsType == null)
|
||||
{
|
||||
throw new Exception($"main class '{clsName}' not found");
|
||||
}
|
||||
|
||||
return clsType;
|
||||
}
|
||||
@ -131,7 +142,7 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
void CallMainMethod(Type mainClass)
|
||||
{
|
||||
var method = mainClass.GetMethod("Main");
|
||||
var method = mainClass.GetMethod("Main") ?? mainClass.GetMethod("main");
|
||||
if (method.IsStatic)
|
||||
{
|
||||
method.Invoke(null, new object[method.GetParameters().Length]);
|
||||
@ -150,6 +161,7 @@ namespace Microsoft.Xna.Framework
|
||||
public void MainLoop(Game game)
|
||||
{
|
||||
int pauseCount = 0;
|
||||
bool clearKeys = false;
|
||||
|
||||
while (game.RunApplication)
|
||||
{
|
||||
@ -197,12 +209,19 @@ namespace Microsoft.Xna.Framework
|
||||
eventBits = shouldEvents.get();
|
||||
|
||||
if ((eventBits & CONFIG_EVENT) != 0)
|
||||
UpdateConfiguration(true);
|
||||
UpdateConfiguration();
|
||||
|
||||
if ((eventBits & TOUCH_EVENT) != 0)
|
||||
{
|
||||
Microsoft.Xna.Framework.Input.Mouse
|
||||
.HandleEvents(clientWidth, clientHeight);
|
||||
.HandleEvents((int) dict["width"], (int) dict["height"]);
|
||||
}
|
||||
|
||||
if ((eventBits & KEY_EVENT) != 0)
|
||||
{
|
||||
Microsoft.Xna.Framework.Input.Keyboard.keys.Add(
|
||||
(Microsoft.Xna.Framework.Input.Keys) eventKeyCode);
|
||||
clearKeys = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,6 +230,16 @@ namespace Microsoft.Xna.Framework
|
||||
//
|
||||
|
||||
game.Tick();
|
||||
|
||||
//
|
||||
// a simulated key is signalled during a single frame
|
||||
//
|
||||
|
||||
if (clearKeys)
|
||||
{
|
||||
clearKeys = false;
|
||||
Microsoft.Xna.Framework.Input.Keyboard.keys.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
InModal = true;
|
||||
@ -263,6 +292,11 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
public void ActivityPause()
|
||||
{
|
||||
if (shouldResume.get() == 0)
|
||||
{
|
||||
Microsoft.Xna.Framework.Media.MediaPlayer.ActivityPauseOrResume(true);
|
||||
Microsoft.Xna.Framework.Audio.SoundEffect.ActivityPauseOrResume(true);
|
||||
|
||||
if (! InModal)
|
||||
{
|
||||
shouldPause.incrementAndGet();
|
||||
@ -271,6 +305,7 @@ namespace Microsoft.Xna.Framework
|
||||
waitForPause.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ActivityResume
|
||||
@ -279,7 +314,16 @@ namespace Microsoft.Xna.Framework
|
||||
public void ActivityResume()
|
||||
{
|
||||
if (shouldResume.compareAndSet(1, 0))
|
||||
{
|
||||
// force a call to UpdateConfiguration after main loop wakes up.
|
||||
// this will not actually invoke callbacks if nothing has changed.
|
||||
OnSurfaceChanged();
|
||||
|
||||
waitForResume.open();
|
||||
|
||||
Microsoft.Xna.Framework.Media.MediaPlayer.ActivityPauseOrResume(false);
|
||||
Microsoft.Xna.Framework.Audio.SoundEffect.ActivityPauseOrResume(false);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -308,6 +352,23 @@ namespace Microsoft.Xna.Framework
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// ActivityKey
|
||||
//
|
||||
|
||||
public void ActivityKey(int keyCode)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
int v = shouldEvents.get();
|
||||
if (shouldEvents.compareAndSet(v, v | KEY_EVENT))
|
||||
{
|
||||
eventKeyCode = keyCode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// OnSurfaceChanged
|
||||
//
|
||||
@ -326,26 +387,76 @@ namespace Microsoft.Xna.Framework
|
||||
// UpdateConfiguration
|
||||
//
|
||||
|
||||
void UpdateConfiguration(bool withCallback)
|
||||
void UpdateConfiguration()
|
||||
{
|
||||
var metrics = new android.util.DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
|
||||
var metrics = GetDisplayMetrics(activity);
|
||||
int width = metrics.widthPixels;
|
||||
int height = metrics.heightPixels;
|
||||
|
||||
clientWidth = metrics.widthPixels;
|
||||
clientHeight = metrics.heightPixels;
|
||||
clientBounds = new Rectangle(0, 0, clientWidth, clientHeight);
|
||||
var dict = new System.Collections.Hashtable();
|
||||
|
||||
if (dict == null)
|
||||
dict = new System.Collections.Hashtable();
|
||||
|
||||
// int dpi - pixels per inch
|
||||
dict["width"] = width;
|
||||
dict["height"] = height;
|
||||
dict["dpi"] = (int) ((metrics.xdpi + metrics.ydpi) * 0.5f);
|
||||
|
||||
if (withCallback)
|
||||
if (object.ReferenceEquals(this.dict, null))
|
||||
{
|
||||
// on first call, set the dict without invoking callbacks
|
||||
SetExtra(dict, width, height);
|
||||
this.dict = dict;
|
||||
}
|
||||
else if (! DictsEqual(dict, this.dict))
|
||||
{
|
||||
SetExtra(dict, width, height);
|
||||
this.dict = dict;
|
||||
|
||||
OnClientSizeChanged();
|
||||
OnOrientationChanged();
|
||||
}
|
||||
|
||||
|
||||
android.util.DisplayMetrics GetDisplayMetrics(android.app.Activity activity)
|
||||
{
|
||||
bool isMultiWindow = false;
|
||||
if (android.os.Build.VERSION.SDK_INT >= 24)
|
||||
isMultiWindow = activity.isInMultiWindowMode();
|
||||
|
||||
var metrics = new android.util.DisplayMetrics();
|
||||
var display = activity.getWindowManager().getDefaultDisplay();
|
||||
if (! isMultiWindow)
|
||||
{
|
||||
// getRealMetrics gets real size of the entire screen.
|
||||
// this is what we need in full screen immersive mode.
|
||||
display.getRealMetrics(metrics);
|
||||
}
|
||||
else
|
||||
{
|
||||
// getMetrics gets the size of the split window, minus
|
||||
// window frames. this is useful in multi-window mode.
|
||||
display.getMetrics(metrics);
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
|
||||
bool DictsEqual(System.Collections.Hashtable dict1,
|
||||
System.Collections.Hashtable dict2)
|
||||
{
|
||||
foreach (var key in dict1.Keys)
|
||||
{
|
||||
if (! dict1[key].Equals(dict2[key]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SetExtra(System.Collections.Hashtable dict, int width, int height)
|
||||
{
|
||||
dict["bounds"] = new Rectangle(0, 0, width, height);
|
||||
dict["openUri"] = (Action<string>) OpenUri;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -359,11 +470,32 @@ namespace Microsoft.Xna.Framework
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// OpenUri
|
||||
//
|
||||
|
||||
private void OpenUri(string uri)
|
||||
{
|
||||
activity.runOnUiThread(((java.lang.Runnable.Delegate) (() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
activity.startActivity(
|
||||
new android.content.Intent(android.content.Intent.ACTION_VIEW,
|
||||
android.net.Uri.parse(uri)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameRunner.Log(e.ToString());
|
||||
}
|
||||
})).AsInterface());
|
||||
}
|
||||
|
||||
//
|
||||
// GameWindow interface
|
||||
//
|
||||
|
||||
public override Rectangle ClientBounds => clientBounds;
|
||||
public override Rectangle ClientBounds => (Rectangle) dict["bounds"];
|
||||
|
||||
public override string ScreenDeviceName => "Android";
|
||||
|
||||
@ -374,8 +506,8 @@ namespace Microsoft.Xna.Framework
|
||||
|
||||
public override DisplayOrientation CurrentOrientation
|
||||
{
|
||||
get => (clientWidth < clientHeight) ? DisplayOrientation.Portrait
|
||||
: DisplayOrientation.LandscapeLeft;
|
||||
get => ((int) dict["width"] < (int) dict["height"])
|
||||
? DisplayOrientation.Portrait : DisplayOrientation.LandscapeLeft;
|
||||
set
|
||||
{
|
||||
bool portrait = 0 != (value & DisplayOrientation.Portrait);
|
||||
@ -387,7 +519,7 @@ namespace Microsoft.Xna.Framework
|
||||
else if (landscape && (! portrait))
|
||||
r = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
|
||||
else
|
||||
r = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
|
||||
return;
|
||||
activity.setRequestedOrientation(r);
|
||||
}
|
||||
}
|
||||
|
@ -201,6 +201,12 @@ namespace Microsoft.Xna.Framework.Input
|
||||
public int Y { get; set; }
|
||||
public ButtonState LeftButton { get; set; }
|
||||
}
|
||||
|
||||
[java.attr.Discard] // discard in output
|
||||
public static class Keyboard
|
||||
{
|
||||
public static List<Keys> keys;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ namespace Microsoft.Xna.Framework.Media
|
||||
[java.attr.RetainType] private static float volume;
|
||||
[java.attr.RetainType] private static bool muted;
|
||||
[java.attr.RetainType] private static bool looping;
|
||||
[java.attr.RetainType] private static bool wasPlayingBeforeActivityPause;
|
||||
|
||||
//
|
||||
// static constructor
|
||||
@ -283,6 +284,28 @@ namespace Microsoft.Xna.Framework.Media
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// ActivityPauseOrResume
|
||||
//
|
||||
|
||||
public static void ActivityPauseOrResume(bool pausing)
|
||||
{
|
||||
if (pausing)
|
||||
{
|
||||
if (State == MediaState.Playing)
|
||||
{
|
||||
wasPlayingBeforeActivityPause = true;
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
else if (wasPlayingBeforeActivityPause)
|
||||
{
|
||||
wasPlayingBeforeActivityPause = false;
|
||||
Resume();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -20,6 +20,8 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
private android.os.ConditionVariable waitObject;
|
||||
private java.util.concurrent.atomic.AtomicInteger paused;
|
||||
private Action actionOnChanged;
|
||||
private int swapInterval;
|
||||
private bool checkErrors;
|
||||
|
||||
public object UserData;
|
||||
|
||||
@ -39,11 +41,14 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
|
||||
private Renderer(android.app.Activity activity, Action onChanged,
|
||||
int redSize, int greenSize, int blueSize,
|
||||
int alphaSize, int depthSize, int stencilSize)
|
||||
int alphaSize, int depthSize, int stencilSize,
|
||||
int swapInterval, bool checkErrors)
|
||||
{
|
||||
waitObject = new android.os.ConditionVariable();
|
||||
paused = new java.util.concurrent.atomic.AtomicInteger();
|
||||
actionOnChanged = onChanged;
|
||||
this.swapInterval = swapInterval;
|
||||
this.checkErrors = checkErrors;
|
||||
|
||||
activity.runOnUiThread(((java.lang.Runnable.Delegate) (() =>
|
||||
{
|
||||
@ -55,7 +60,6 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
surface.setRenderer(this);
|
||||
surface.setRenderMode(android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||
activity.setContentView(surface);
|
||||
|
||||
})).AsInterface());
|
||||
|
||||
// wait for one onDrawFrame callback, which tells us that
|
||||
@ -78,16 +82,21 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
// Send
|
||||
//
|
||||
|
||||
public void Send(Action action)
|
||||
public void Send(bool wait, Action action)
|
||||
{
|
||||
Exception exc = null;
|
||||
if (paused.get() == 0)
|
||||
{
|
||||
var cond = new android.os.ConditionVariable();
|
||||
surface.queueEvent(((java.lang.Runnable.Delegate) (() =>
|
||||
if (! waitObject.block(2000))
|
||||
{
|
||||
var error = GLES20.glGetError();
|
||||
if (error == GLES20.GL_NO_ERROR)
|
||||
// see also Present(). a timeout here means that onDrawFrame
|
||||
// was never called, so the surface was probably destroyed.
|
||||
paused.compareAndSet(0, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
var cond = wait ? new android.os.ConditionVariable() : null;
|
||||
surface.queueEvent(((java.lang.Runnable.Delegate) (() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -97,18 +106,21 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
exc = exc2;
|
||||
}
|
||||
error = GLES20.glGetError();
|
||||
}
|
||||
if (checkErrors)
|
||||
{
|
||||
var error = GLES20.glGetError();
|
||||
if (error != GLES20.GL_NO_ERROR)
|
||||
exc = new Exception($"GL Error {error}");
|
||||
}
|
||||
if (cond != null)
|
||||
cond.open();
|
||||
})).AsInterface());
|
||||
if (cond != null)
|
||||
cond.block();
|
||||
}
|
||||
if (exc != null)
|
||||
{
|
||||
throw new AggregateException(exc.Message, exc);
|
||||
}
|
||||
if (exc != null)
|
||||
throw new AggregateException(exc.Message, exc);
|
||||
}
|
||||
|
||||
//
|
||||
@ -119,7 +131,19 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
waitObject.close();
|
||||
surface.requestRender();
|
||||
waitObject.block();
|
||||
|
||||
// GLSurfaceView runs queued events before checking if render
|
||||
// was requested; but if the event queue is empty, it checks
|
||||
// if render requested, then calls onDrawFrame(). thus the
|
||||
// sequence of events is as follows:
|
||||
|
||||
// - Present() blocks waitObject and requests render
|
||||
// - any events already queued by Send() are processed by
|
||||
// GLSurfaceView, before it checks for a requested render.
|
||||
// - Send() delays any new events until waitObject is opened.
|
||||
// - OnDrawFrame() is called and opens waitObject, and when
|
||||
// it returns to GLSurfaceView, the GL buffers are swapped.
|
||||
// - with waitObject open, Send() queues new events.
|
||||
}
|
||||
|
||||
//
|
||||
@ -132,6 +156,17 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
// if onSurfaceCreated is called while resuming from pause,
|
||||
// it means the GL context was lost
|
||||
paused.compareAndSet(1, -1);
|
||||
|
||||
// assign high priority to the rendering thread
|
||||
java.lang.Thread.currentThread()
|
||||
.setPriority(java.lang.Thread.MAX_PRIORITY);
|
||||
|
||||
// set swap interval
|
||||
if (swapInterval != 1)
|
||||
{
|
||||
var eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
|
||||
EGL14.eglSwapInterval(eglDisplay, swapInterval);
|
||||
}
|
||||
}
|
||||
|
||||
[java.attr.RetainName]
|
||||
@ -188,7 +223,8 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
|
||||
public static IntPtr Create(android.app.Activity activity, Action onChanged,
|
||||
int redSize, int greenSize, int blueSize,
|
||||
int alphaSize, int depthSize, int stencilSize)
|
||||
int alphaSize, int depthSize, int stencilSize,
|
||||
int swapInterval, bool checkErrors)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
@ -216,7 +252,8 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
deviceId = deviceId,
|
||||
renderer = new Renderer(activity, onChanged,
|
||||
redSize, greenSize, blueSize,
|
||||
alphaSize, depthSize, stencilSize),
|
||||
alphaSize, depthSize, stencilSize,
|
||||
swapInterval, checkErrors),
|
||||
activity = new java.lang.@ref.WeakReference(activity),
|
||||
});
|
||||
|
||||
@ -290,7 +327,7 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
foreach (var renderer in GetRenderersForActivity(activity))
|
||||
{
|
||||
renderer.surface.onPause();
|
||||
renderer.paused.set(1);
|
||||
renderer.paused.compareAndSet(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,9 +341,20 @@ namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
if (renderer.paused.get() != 0)
|
||||
{
|
||||
renderer.waitObject.close();
|
||||
// we cannot use waitObject for resuming, because the surface
|
||||
// may have been destroyed between pause and resume, in which
|
||||
// case onDrawFrame would not get called.
|
||||
|
||||
var cond = new android.os.ConditionVariable();
|
||||
renderer.surface.queueEvent(((java.lang.Runnable.Delegate) (
|
||||
() => cond.open() )).AsInterface());
|
||||
|
||||
renderer.surface.onResume();
|
||||
renderer.waitObject.block();
|
||||
if (! cond.block(2000))
|
||||
{
|
||||
// something is wrong if the queued event did not run
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! renderer.paused.compareAndSet(1, 0))
|
||||
{
|
||||
|
@ -264,6 +264,29 @@ namespace Microsoft.Xna.Framework.Audio
|
||||
public static float DistanceScale { get; set; }
|
||||
public static float DopplerScale { get; set; }
|
||||
public static float SpeedOfSound { get; set; }
|
||||
|
||||
//
|
||||
// ActivityPauseOrResume
|
||||
//
|
||||
|
||||
public static void ActivityPauseOrResume(bool pausing)
|
||||
{
|
||||
try
|
||||
{
|
||||
instancesLock.@lock();
|
||||
int num = instancesList.size();
|
||||
for (int idx = 0; idx < num; idx++)
|
||||
{
|
||||
var instRef = (java.lang.@ref.WeakReference) instancesList.get(idx);
|
||||
((SoundEffectInstance) instRef.get())?.ActivityPauseOrResume(pausing);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
instancesLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -279,6 +302,7 @@ namespace Microsoft.Xna.Framework.Audio
|
||||
[java.attr.RetainType] private android.media.AudioTrack track;
|
||||
[java.attr.RetainType] private float pitch, pan, volume;
|
||||
[java.attr.RetainType] private bool isLooped;
|
||||
[java.attr.RetainType] private bool wasPlayingBeforeActivityPause;
|
||||
|
||||
//
|
||||
// Constructor (for SoundEffect.CreateInstance)
|
||||
@ -595,6 +619,27 @@ namespace Microsoft.Xna.Framework.Audio
|
||||
public void Apply3D(AudioListener listener, AudioEmitter emitter) { }
|
||||
public void Apply3D(AudioListener[] listeners, AudioEmitter emitter) { }
|
||||
|
||||
//
|
||||
// ActivityPauseOrResume
|
||||
//
|
||||
|
||||
public void ActivityPauseOrResume(bool pausing)
|
||||
{
|
||||
if (pausing)
|
||||
{
|
||||
if (State == SoundState.Playing)
|
||||
{
|
||||
wasPlayingBeforeActivityPause = true;
|
||||
Pause();
|
||||
}
|
||||
}
|
||||
else if (wasPlayingBeforeActivityPause)
|
||||
{
|
||||
wasPlayingBeforeActivityPause = false;
|
||||
Resume();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,10 @@
|
||||
package="com.spaceflint.bluebonnet.xnademo1">
|
||||
|
||||
<!-- API 18, GLES 3.0 -->
|
||||
<!-- note that if testing in the Android emulator, you may need to add
|
||||
the following line in your ~/.android/advancedFeatures.ini file:
|
||||
GLESDynamicVersion=on
|
||||
!-->
|
||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="29" />
|
||||
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
|
||||
|
||||
@ -15,7 +19,10 @@
|
||||
|
||||
<application android:label="BNA_Demo1"
|
||||
android:icon="@drawable/icon"
|
||||
android:isGame="true" >
|
||||
android:isGame="true"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
>
|
||||
|
||||
<!-- set android:screenOrientation if you need to lock orientation:
|
||||
https://developer.android.com/guide/topics/manifest/activity-element#screen -->
|
||||
@ -29,21 +36,38 @@
|
||||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
|
||||
android:immersive="true"
|
||||
android:launchMode="singleTask"
|
||||
android:maxAspectRatio="9"
|
||||
>
|
||||
|
||||
<!-- microsoft.xna.framework.log.tag sets the log identifier
|
||||
for log messages printed via android.util.Log by the app.
|
||||
if omitted, the default is 'BNA_Game' -->
|
||||
<!-- the name of the main entrypoint class. if starts with a dot,
|
||||
then it is appended to the namespace specifies in the package
|
||||
attribute. the default is '.Program' -->
|
||||
<meta-data android:name="BNA.main.class" android:value=".Program"/>
|
||||
|
||||
<meta-data android:name="microsoft.xna.framework.log.tag"
|
||||
android:value="BNA_Demo1"/>
|
||||
<!-- the log identifier for log messages printed via android.util.Log
|
||||
by the app. the default is 'BNA_Game' -->
|
||||
<meta-data android:name="BNA.log.tag" android:value="BNA_Demo1"/>
|
||||
|
||||
<!-- microsoft.xna.framework.main.class sets the name of the main
|
||||
or entrypoint class. if starts with a dot, it is appended
|
||||
to the namespace specifies in the package attribute. -->
|
||||
<!-- specifies whether to call glGetError() after every GL call.
|
||||
non-zero value enables error checks, zero skips error checks.
|
||||
this has a negative impact on performance, and is not recommended
|
||||
in production builds. the default is '0' -->
|
||||
<meta-data android:name="BNA.check.gl.errors" android:value="0"/>
|
||||
|
||||
<meta-data android:name="microsoft.xna.framework.main.class"
|
||||
android:value=".Program"/>
|
||||
<!-- specifies whether to disable screen timeout, if set to a non-zero
|
||||
value. the default is '0' -->
|
||||
<meta-data android:name="BNA.keep.screen.on" android:value="0"/>
|
||||
|
||||
<!-- specifies whether to enable full screen immersive mode, if set
|
||||
to a non-zero value. the default is '0' -->
|
||||
<meta-data android:name="BNA.immersive.mode" android:value="0"/>
|
||||
|
||||
<!-- specifies the key to signal, for one frame, when the back button
|
||||
is pressed. if omitted or specified as zero, default handling of
|
||||
the back button occurs, which typically stops the activity.
|
||||
Escape = Microsoft.Xna.Framework.Input.Keys.Escape = 27.
|
||||
Back = Microsoft.Xna.Framework.Input.Keys.Back = 8. -->
|
||||
<meta-data android:name="BNA.back.key" android:value="0"/>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@ -51,6 +75,15 @@
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="9" />
|
||||
|
||||
</application>
|
||||
|
||||
<supports-screens android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:xlargeScreens="true"
|
||||
android:anyDensity="true" />
|
||||
|
||||
</manifest>
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
APK_TEMP_DIR = directory where APK processing occurs
|
||||
e.g. $(OutputDir)/MyGame/Debug/Content
|
||||
IMPORTANT: this directory will be deleted and recreated!
|
||||
|
||||
KEYSTORE_FILE = path to a keystore file used in APK signing
|
||||
|
||||
@ -33,8 +34,6 @@
|
||||
|
||||
BLUEBONNET_EXE = path to Bluebonnet.exe program file
|
||||
|
||||
BLUEBONNET_JAR = path to Baselib.jar file from Bluebonnet
|
||||
|
||||
ANDROID_JAR = path to Android.jar file, for desired API level
|
||||
e.g. $(ANDROID_HOME)/android-28/android.jar
|
||||
|
||||
|
24
build_bna.bat
Normal file
24
build_bna.bat
Normal file
@ -0,0 +1,24 @@
|
||||
@echo off
|
||||
if "%ANDROID_JAR%" == "" (
|
||||
echo Missing environment variable ANDROID_JAR.
|
||||
echo It should specify the full path to an Android.jar file in the platforms directory of the Android SDK.
|
||||
goto :EOF
|
||||
)
|
||||
if "%FNA_DLL%" == "" (
|
||||
echo Missing environment variable FNA_DLL.
|
||||
echo It should specify the full path to the FNA.DLL file.
|
||||
goto :EOF
|
||||
)
|
||||
if "%BLUEBONNET_EXE%" == "" (
|
||||
echo Missing environment variable BLUEBONNET_EXE.
|
||||
echo It should specify the full path to the Bluebonnet executable.
|
||||
goto :EOF
|
||||
)
|
||||
|
||||
echo ========================================
|
||||
echo Building BNA. Command:
|
||||
echo MSBuild BNA -p:Configuration=Release
|
||||
echo ========================================
|
||||
MSBuild BNA -p:Configuration=Release
|
||||
|
||||
:EOF
|
Loading…
x
Reference in New Issue
Block a user